この大会は2023/10/13 22:00(JST)~2023/10/15 22:00(JST)に開催されました。
今回もチームで参戦。結果は1779点で499チーム中33位でした。
自分で解けた問題をWriteupとして書いておきます。
hide and split (Forensic)
FTK Imagerで開き、flag00.txt~flag99.txtを見る。ADSが設定されているので、1つ1つ書き出す。
flag00 89504e470d0a1a0a0000000d49484452000003390000033908000000000179a0c500000e684 flag01 944415478daeddbcb61e4300c44c1c93f696f0cb3ea0620bbde55b63e248ab7f9fc48fabe8f flag02 2590c891c891c891c891448e448e448e448e448e24722472247224722472249123912391239 flag03 1239123891c891c891c891c891c49e448e448e448e448e44822472247224722472247123912 flag04 3912391239123992c891c891c891c891c891448e448e448e448e448e2472247224722472247 flag05 22491239123912391239123891c891c891c891c891c49e448e448e448e448e4482247224722 flag06 47224722e7ffee35d557cf0d7e51f08fc76e157cee93757e72e7dee490430e39e490430e39e flag07 490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e49043 flag08 0e39e490430e39e4903326e7e69d7b269f5c3d223678e75ff009e490430e39e490430e39e49 flag09 0430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e flag10 39e490430e39e4f4de32f80b9cb1ab4f203df9fce0e2047761eba4185b1c72c821871c72c82 flag11 1871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c flag12 72c821871c72c821871c727e819cde5bdd4418fc84e0ff8e2d2c39e490430e39e490430e39e flag13 490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e49043 flag14 0e39e490430e39e45cd8a420a44fadde839e0026871c72c821871c72c821871c72c821871c7 flag15 2c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821 flag16 879c953b3fd9fe1eb3e05af5063a08e98d93430e39e490430e39e490430e39e490430e39e49 flag17 0430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490434e flag18 ef2db77e82e2eadbafbe433b39ae92438eabe490430e39e490e32a39e4b84a0e39ae92438ea flag19 be490430e39e490e32a39e4b84a0e39ae92438eabe490430e39e490e32a3927bb390d5f0d47 flag20 f0841a3bdd9eacc66f983a72c821871c72c821871c72c821871c72c821871c72c821871c72c flag21 821871c72c821871c72c821871c72c821871c72c821871c72c821871c72963004e7ecc983c6 flag22 a67f6ba07b7f7cf3ec23871c72c821871c72c821871c72c821871c72c821871c72c821871c7 flag23 2c821871c72c821871c72c821871c72c821871c72c821871c72c821674cce93fd0eba0afef1 flag24 934fd8021cfcde23f88383440e39e490430e39e490430e39e490430e39e490430e39e490430 flag25 e39e490430e39e490430e39e490430e39e490430e39e490430e39e490434e4fced642f7d6ae flag26 f7a0b1c1baa96eec7423871c72c821871c72c821871c72c821871c72c821871c72c821871c7 flag27 2c821871c72c821871c72c821871c72c821871c72c821871c72c821e715728eac5df07fb720 flag28 0547e7e671169c1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 flag29 c72c821871c72c821871c72c821871c72c821871c72c821871c72b6e4f4063a48b4b767c1ff flag30 1d3b9282a74c6f9dc78e3372c821871c72c821871c72c821871c72c821871c72c821871c72c flag31 821871c72c821871c72c821871c72c821871c72c821871c72c821871c727a905ec12cb8eec1 flag32 e77e72053f7f6c7f7b870e39e490430e39e490430e39e490430e39e490430e39e490430e39e flag33 490430e39e490430e39e490430e39e490430e39e490430e39e490430e395b72be5ae89b8b35 flag34 863f78b5877f6c718263460e39e490430e39e490430e39e490430e39e490430e39e490430e3 flag35 9e490430e39e490430e39e490430e39e490430e39e490430e39e49043ce989ce042f77625f8 flag36 bf3d0c5b4bd7b3d13b65b68e5172c821871c72c821871c72c821871c72c821871c72c821871 flag37 c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7282bb7273dc7b flag38 9b149c95def935867f0cf04f2d72c821871c72c821871c72c821871c72c821871c72c821871 flag39 c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c726e421a9ba457 flag40 8cddd8e26c1d7663e72639e490430e39e490430e39e490430e39e490430e39e490430e39e49 flag41 0430e39e490430e39e490430e39e490430e39e490430e39e490430e3963a333b60dc1817ef2 flag42 a0573cf7e6c26e1d0de490430e39e490430e39e490430e39e490430e39e490430e39e490430 flag43 e39e490430e39e490430e39e490430e39e490430e39e490430e39e4f4a804372978e74fadde flag44 b17264be7b5bd6db2372c821871c72c821871c72c821871c72c821871c72c821871c72c8218 flag45 71c72c821871c72c821871c72c821871c72c821871c72c821871c72c6e4f4c63d68636b0b6f flag46 8aed7dc2d6f493430e39e490430e39e490430e39e490430e39e490430e39e490430e39e4904 flag47 30e39e490430e39e490430e39e490430e39e490430e39e490f30a39c1d5f9cac6d86bf4de39 flag48 7816f44e9957ec2f39e490430e39e490430e39e490430e39e490430e39e490430e39e490430 flag49 e39e490430e39e490430e39e490430e39e490430e39e490430e3937e5f4ae1617ebc1838e4c flag50 708fd998d8addd27871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 flag51 1871c72c821871c72c821871c72c821871c72c821871c72c821676bb082cfdd82d47bab372e flag52 ecd66b14779f1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 flag53 2c821871c72c821871c72c821871c72c821871c72c821879c9363175c9db14d1afbdee09db7 flag54 5ea337fde490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430 flag55 e39e490430e39e490430e39e490430e39e490430e39e4f4e4049732f8a0affef733d598d8de flag56 f706f777cc2439e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e flag57 490430e39e490430e39e490430e39e490430e39e490430e3963728223dba332a6eec8246ded flag58 6f6f438f440e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e flag59 490430e39e490430e39e490430e39e490430e39e490434e7029c77e917253ce912fea21ec2d flag60 fbd66a90430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e flag61 490430e39e490430e39e490430e39e490430e39e490d35bf7de7204ff788bf7cd09de3279f4 flag62 13c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 flag63 1871c72c821871c72c821871c72c821871c72c8a9ad4e6f657b807b031d3c38b68e8623d3df flag64 7b1039e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e3 flag65 9e490430e39e490430e39e490430e39e490430e39c16fe8d5dbc21ea42343195cf6adb38f1c flag66 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 flag67 1871c72c821871c72c821871c72c821879c2372befa86e00407df6a8b4af07b8f1c765bdac9 flag68 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 flag69 c72c821871c72c821871c72c821871c72c8b929e7c9af2cc6f6ec8d07c791a1dc5ad8ad7384 flag70 1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c flag71 821871c72c821871c72c821871c72c821879cdf373a5b83d53b659eccd9d87136f6bde49043 flag72 0e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e flag73 490430e39e490430e39e490430e39e41c917344dd131bc10dde9aef37eee0d8b1420e39e490 flag74 430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e3 flag75 9e490430e39e490430e39e49043ce969cde7c8fedd9d8178d897d3267639f7f64aec821871c flag76 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 flag77 1871c72c821871c72c821871c72c8095279f2fdc13b3fb9d5d86b6c4dc3d82ef4beb767831c flag78 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 flag79 1871c72c821871c72c821871c72c821879c37169cb327e31e9cd1b1713f728e6cf126871c72 flag80 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 flag81 71c72c821871c72c821871c72c821a727e733d5cdb71afbcdc9d84a06cf91b1abe490430e39 flag82 e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e4904 flag83 30e39e490430e39e490430e39e4dc943376e73157c1cfefbdf3d6a61c39fb7ea622871c72c8 flag84 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 flag85 c72c821871c72c821871c72c8212738dfbded1f7b8ddeaf4a7a2fb9a5aef7bd4ff6881c72c8 flag86 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 flag87 c72c821871c72c821871c72c821871c7252368e3c77ec137aae7accc821871c72c821871c72 flag88 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 flag89 71c72c821871c72c8f9e37282d3308621b87463df1b7c50eff3c921871c72c821871c72c821 flag90 871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 flag91 2c821871c72c8b929e7c89db7c48e6dffd649f1e42ce81d49bdc821871c72c821871c72c821 flag92 871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 flag93 2c821871c72c8d99a95b1d1b9c96cec0c0abec6d6f48f4122871c72c821871c72c821871c72 flag94 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 flag95 71c72c821471239123912391239123992c891c891c891c891c891448e448e448e448e448e24 flag96 7224722472247224722491239123912391239123891c891c891c891c891c49e448e448e448e flag97 448e448224722472247224722471239123912391239123992c891c891c891c891c891448e44 flag98 8e448e448e448e247224722472247224722491239123912391239123891c891c891c891c891 flag99 c49e448e448e448e4487faf7fd99fb41dd95101ee0000000049454e44ae426082
このデータを連結してデコードし、バイナリにすると、pngファイルになりそう。
#!/usr/bin/env python3 data = ''' 89504e470d0a1a0a0000000d49484452000003390000033908000000000179a0c500000e684 944415478daeddbcb61e4300c44c1c93f696f0cb3ea0620bbde55b63e248ab7f9fc48fabe8f 2590c891c891c891c891448e448e448e448e448e24722472247224722472249123912391239 1239123891c891c891c891c891c49e448e448e448e448e44822472247224722472247123912 3912391239123992c891c891c891c891c891448e448e448e448e448e2472247224722472247 22491239123912391239123891c891c891c891c891c49e448e448e448e448e4482247224722 47224722e7ffee35d557cf0d7e51f08fc76e157cee93757e72e7dee490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e49043 0e39e490430e39e4903326e7e69d7b269f5c3d223678e75ff009e490430e39e490430e39e49 0430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e 39e490430e39e4f4de32f80b9cb1ab4f203df9fce0e2047761eba4185b1c72c821871c72c82 1871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c 72c821871c72c821871c727e819cde5bdd4418fc84e0ff8e2d2c39e490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e49043 0e39e490430e39e45cd8a420a44fadde839e0026871c72c821871c72c821871c72c821871c7 2c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821 879c953b3fd9fe1eb3e05af5063a08e98d93430e39e490430e39e490430e39e490430e39e49 0430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490434e ef2db77e82e2eadbafbe433b39ae92438eabe490430e39e490e32a39e4b84a0e39ae92438ea be490430e39e490e32a39e4b84a0e39ae92438eabe490430e39e490e32a3927bb390d5f0d47 f0841a3bdd9eacc66f983a72c821871c72c821871c72c821871c72c821871c72c821871c72c 821871c72c821871c72c821871c72c821871c72c821871c72c821871c72963004e7ecc983c6 a67f6ba07b7f7cf3ec23871c72c821871c72c821871c72c821871c72c821871c72c821871c7 2c821871c72c821871c72c821871c72c821871c72c821871c72c821674cce93fd0eba0afef1 934fd8021cfcde23f88383440e39e490430e39e490430e39e490430e39e490430e39e490430 e39e490430e39e490430e39e490430e39e490430e39e490430e39e490434e4fced642f7d6ae f7a0b1c1baa96eec7423871c72c821871c72c821871c72c821871c72c821871c72c821871c7 2c821871c72c821871c72c821871c72c821871c72c821871c72c821e715728eac5df07fb720 0547e7e671169c1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c821871c72c821871c72c821871c72b6e4f4063a48b4b767c1ff 1d3b9282a74c6f9dc78e3372c821871c72c821871c72c821871c72c821871c72c821871c72c 821871c72c821871c72c821871c72c821871c72c821871c72c821871c727a905ec12cb8eec1 e77e72053f7f6c7f7b870e39e490430e39e490430e39e490430e39e490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490430e39e490430e395b72be5ae89b8b35 863f78b5877f6c718263460e39e490430e39e490430e39e490430e39e490430e39e490430e3 9e490430e39e490430e39e490430e39e490430e39e490430e39e49043ce989ce042f77625f8 bf3d0c5b4bd7b3d13b65b68e5172c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7282bb7273dc7b 9b149c95def935867f0cf04f2d72c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c726e421a9ba457 8cddd8e26c1d7663e72639e490430e39e490430e39e490430e39e490430e39e490430e39e49 0430e39e490430e39e490430e39e490430e39e490430e39e490430e3963a333b60dc1817ef2 a0573cf7e6c26e1d0de490430e39e490430e39e490430e39e490430e39e490430e39e490430 e39e490430e39e490430e39e490430e39e490430e39e490430e39e4f4a804372978e74fadde b17264be7b5bd6db2372c821871c72c821871c72c821871c72c821871c72c821871c72c8218 71c72c821871c72c821871c72c821871c72c821871c72c821871c72c6e4f4c63d68636b0b6f 8aed7dc2d6f493430e39e490430e39e490430e39e490430e39e490430e39e490430e39e4904 30e39e490430e39e490430e39e490430e39e490430e39e490f30a39c1d5f9cac6d86bf4de39 7816f44e9957ec2f39e490430e39e490430e39e490430e39e490430e39e490430e39e490430 e39e490430e39e490430e39e490430e39e490430e39e490430e3937e5f4ae1617ebc1838e4c 708fd998d8addd27871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 1871c72c821871c72c821871c72c821871c72c821871c72c821676bb082cfdd82d47bab372e ecd66b14779f1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 2c821871c72c821871c72c821871c72c821871c72c821879c9363175c9db14d1afbdee09db7 5ea337fde490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430 e39e490430e39e490430e39e490430e39e490430e39e4f4e4049732f8a0affef733d598d8de f706f777cc2439e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490430e3963728223dba332a6eec8246ded 6f6f438f440e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490434e7029c77e917253ce912fea21ec2d fbd66a90430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e 490430e39e490430e39e490430e39e490430e39e490d35bf7de7204ff788bf7cd09de3279f4 13c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 1871c72c821871c72c821871c72c821871c72c8a9ad4e6f657b807b031d3c38b68e8623d3df 7b1039e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e3 9e490430e39e490430e39e490430e39e490430e39c16fe8d5dbc21ea42343195cf6adb38f1c 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 1871c72c821871c72c821871c72c821879c2372befa86e00407df6a8b4af07b8f1c765bdac9 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c821871c72c8b929e7c9af2cc6f6ec8d07c791a1dc5ad8ad7384 1c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c 821871c72c821871c72c821871c72c821879cdf373a5b83d53b659eccd9d87136f6bde49043 0e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e 490430e39e490430e39e490430e39e41c917344dd131bc10dde9aef37eee0d8b1420e39e490 430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e3 9e490430e39e490430e39e49043ce969cde7c8fedd9d8178d897d3267639f7f64aec821871c 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 1871c72c821871c72c821871c72c8095279f2fdc13b3fb9d5d86b6c4dc3d82ef4beb767831c 72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c82 1871c72c821871c72c821871c72c821879c37169cb327e31e9cd1b1713f728e6cf126871c72 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 71c72c821871c72c821871c72c821a727e733d5cdb71afbcdc9d84a06cf91b1abe490430e39 e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e490430e39e4904 30e39e490430e39e490430e39e4dc943376e73157c1cfefbdf3d6a61c39fb7ea622871c72c8 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c8212738dfbded1f7b8ddeaf4a7a2fb9a5aef7bd4ff6881c72c8 21871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871 c72c821871c72c821871c72c821871c7252368e3c77ec137aae7accc821871c72c821871c72 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 71c72c821871c72c8f9e37282d3308621b87463df1b7c50eff3c921871c72c821871c72c821 871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 2c821871c72c8b929e7c89db7c48e6dffd649f1e42ce81d49bdc821871c72c821871c72c821 871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c7 2c821871c72c8d99a95b1d1b9c96cec0c0abec6d6f48f4122871c72c821871c72c821871c72 c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c821871c72c8218 71c72c821471239123912391239123992c891c891c891c891c891448e448e448e448e448e24 7224722472247224722491239123912391239123891c891c891c891c891c49e448e448e448e 448e448224722472247224722471239123912391239123992c891c891c891c891c891448e44 8e448e448e448e247224722472247224722491239123912391239123891c891c891c891c891 c49e448e448e448e4487faf7fd99fb41dd95101ee0000000049454e44ae426082 ''' data = data.replace('\n', '') flag = bytes.fromhex(data) with open('flag.png', 'wb') as f: f.write(flag)
生成したpng画像はQRコードになっている。QRコードを読み取ると、フラグが得られた。
TCP1P{hidden_flag_in_the_extended_attributes_fea73c5920aa8f1c}
scrambled egg (Forensic)
png画像のバイナリの順序を以下のような流れで変更している。
・new = [b'\x89PNG\r\n\x1a\n'] ・IHDRチャンク以降の各チャンクについて以下を実行 ・size: chunkのサイズを示すバイナリ文字列 ・chunk: sizeの順序を逆にしたもの ・chunk: チャンク名 + chunk ・chunk: チャンクデータ + chunk ・newに追加 ・newの順序を逆にする。
IENDチャンクから順に抽出し、元に戻していく。なおCRCの情報は含まれていないので、算出して設定していく。
#!/usr/bin/env python3 import struct import binascii ## research ## chunks = [b'IEND', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IHDR', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT', b'IDAT' ] with open('scrambled.png', 'rb') as f: new = f.read() flags = [] i = 0 for chunk in chunks: if new[i:i+8] == b'\x89PNG\r\n\x1a\n': flags.append(new[i:i+8]) i += 8 else: index = new.index(chunk, i) size = new[index+4:index+8][::-1] i_size = struct.unpack('>I', size)[0] body = new[index:index+4] + new[i:i+i_size] assert i+i_size == index # for research org = size + body crc =struct.pack('!I', binascii.crc32(body)) flags.append(org + crc) i += i_size + 8 ## for research ## #print(hex(i)) ## order adjustment ## reverse_flags = [] round = len(flags) for i in range(round): flag = flags.pop(0) reverse_flags.append(flag) flags = flags[::-1] flag = b''.join(reverse_flags[::-1]) with open('flag.png', 'wb') as f: f.write(flag)
復元した画像にフラグが書いてあった。
TCP1P{y0Ur_u5u4L_pN9_cHUnk_ch4LL}
One Pad Time (Cryptography)
フラグをAES-CBCで暗号化した後にパディングし、keyとXORしている。パディング文字列は b'\x10' * 16 とわかっているので、対応するブロックの暗号化とXORすれば、keyがわかる。あとはそれを使って復号すればよい。
#!/usr/bin/env python3 from pwn import xor from Crypto.Cipher import AES from Crypto.Util.Padding import unpad with open('output.txt', 'r') as f: params = f.read().splitlines() iv = eval(params[0].split(' = ')[1]) ct = eval(params[1].split(' = ')[1]) key = xor(b'\x10' * 16, ct[-16:]) ct = xor(ct, key) cipher = AES.new(key, AES.MODE_CBC, iv) flag = cipher.decrypt(unpad(ct, 16)).decode() print(flag)
TCP1P{why_did_the_chicken_cross_the_road?To_ponder_the_meaning_of_life_on_the_other_side_only_to_realize_that_the_road_itself_was_an_arbitrary_construct_with_no_inherent_purpose_and_that_true_enlightenment_could_only_be_found_within_its_own_existence_1234}
Spider Shambles (Cryptography)
Mersenne Twisterの特徴を使って、flago.jpgの暗号化時のXORキーを算出し、復号する。Mersenne Twisterでは624個の32bit整数から次のランダム値を推測できる。送信データの最大値は16 * 1024 * 1024で収まりそう。
$ python3 -c "print('A' * 2496, end='')" > test.txt
この2496バイトのファイルをアップロードし、暗号化データファイルを取得する。その後、http://ctf.tcp1p.com:54734/flagoにアクセスし、flago.jpgの暗号化データファイルを取得する。あとはMersenne Twisterの特徴から鍵を算出し、flago.jpgを復号する。
#!/usr/bin/env python3 import random from Crypto.Util.number import * def xor(a, b): return b''.join([bytes([_a ^ _b]) for _a, _b in zip(a, b)]) def untemper(rand): rand ^= rand >> 18; rand ^= (rand << 15) & 0xefc60000; a = rand ^ ((rand << 7) & 0x9d2c5680); b = rand ^ ((a << 7) & 0x9d2c5680); c = rand ^ ((b << 7) & 0x9d2c5680); d = rand ^ ((c << 7) & 0x9d2c5680); rand = rand ^ ((d << 7) & 0x9d2c5680); rand ^= ((rand ^ (rand >> 11)) >> 11); return rand pt = b'A' * 2496 with open('lalalalululu', 'rb') as f: ct = f.read() key = bytes_to_long(xor(pt, ct)) N = 624 state = [] for i in range(624): v = untemper((key >> (32 * i)) & 0xffffffff) state.append(v) state.append(N) random.setstate([3, tuple(state), None]) with open('babababububu', 'rb') as f: ct = f.read() key = random.getrandbits(len(ct) * 8) flag = xor(ct, long_to_bytes(key)) with open('flago.jpg', 'wb') as f: f.write(flag)
復号した画像にフラグが書いてあった。
TCP1P{life's_twisted_like_a_back_road_in_the_country}
Final Consensus (Cryptography)
サーバの処理概要は以下の通り。
・a: ランダム0以上999999以下の数値文字列の6バイト0パディングした文字列4つの先頭16バイト ・b: ランダム0以上999999以下の数値文字列の6バイト0パディングした文字列4つの先頭16バイト ・encrypt(FLAG, a, b)を表示 ・ct: FLAGをパディングしたものをAES-ECB暗号化(鍵はa) ・ct: ctをAES-ECB暗号化(鍵はb) ・ctの16進数表記文字列を返却 ・plain: 入力 ・encrypt(plain.encode(), a, b)を表示
$ nc ctf.tcp1p.com 35257 Alice: My message fe196b526678396cd93d68d4ead8e2770766b8984980b4260290a34afdc97594863a388c3c952992cb8e99ae4590ebeebd390be72a204ff5d5a431afe67162f24936a7e11dd997acd5c5f58f50549c0e Alice: Now give me yours! >> test Steve: ff6ab4ca4f4b8bb5e572eafa8ae98965 Alice: Agree.
"test"を暗号化したものと、"ff6ab4ca4f4b8bb5e572eafa8ae98965"を復号したものが一致する鍵の組み合わせを探す。あとはその鍵を使ってフラグを復号する。
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad flag_enc = 'fe196b526678396cd93d68d4ead8e2770766b8984980b4260290a34afdc97594863a388c3c952992cb8e99ae4590ebeebd390be72a204ff5d5a431afe67162f24936a7e11dd997acd5c5f58f50549c0e' flag_enc = bytes.fromhex(flag_enc) try_pt = b'test' try_ct = bytes.fromhex('ff6ab4ca4f4b8bb5e572eafa8ae98965') encs = [] for i in range(1000000): a = (str(i).zfill(6) * 4)[:16].encode() cipher = AES.new(a, mode=AES.MODE_ECB) ct = cipher.encrypt(pad(try_pt, 16)) encs.append(ct) for i in range(1000000): b = (str(i).zfill(6) * 4)[:16].encode() cipher = AES.new(b, mode=AES.MODE_ECB) ct = cipher.decrypt(try_ct) if ct in encs: index = encs.index(ct) a = (str(index).zfill(6) * 4)[:16].encode() break cipher = AES.new(b, mode=AES.MODE_ECB) ct = cipher.decrypt(flag_enc) cipher = AES.new(a, mode=AES.MODE_ECB) FLAG = unpad(cipher.decrypt(ct), 16).decode() print(FLAG)
TCP1P{nothing_ever_lasts_forever_everybody_wants_to_rule_the_world}
Cherry Leak (Cryptography)
サーバの処理概要は以下の通り。
・p: 1024ビット素数 ・q: 6512ビット素数 ・n = p * q ・e = 65537 ・lock = False ・以下繰り返し ・choice: メニュー選択(数値入力) ・choiceが1の場合 ・lockがTrueの場合 ・"You can't do that anymore!"を表示 ・メニュー選択に戻る ・prime: 入力 ・primeが"p"の場合 ・p: 1024ビット素数 ・primeが"q"の場合 ・q: 512ビット素数 ・primeが"p","q"以外の場合 ・"What?"を表示 ・メニュー選択に戻る ・n = p * q ・lock = True ・choiceが2の場合 ・leak: 入力 ・leakが"+"の場合 ・pow(p + q, e, n)を表示 ・leakが"-"の場合 ・p - qを表示 ・leakが"*"の場合 ・pow(p * q, e, n)を表示 ・leakが"/"の場合 ・pow(p // q, e, n)を表示 ・leakが"%"の場合 ・p % qを表示 ・leakが"+","-","*","/","%"以外の場合 ・"What?"を表示 ・choiceが3の場合 ・pow(bytes_to_long(FLAG), e, n)を表示 ・lock = True ・choiceが4の場合 ・終了 ・choiceが1,2,3,4以外の場合 ・"What?"を表示
pow関数を使っていないものに注目する。
p - q = A p % q = B ↓ p = q * X + B p - q = q * (X - 1) + B ↓ A = q * (X - 1) + B
pを変更すれば、以下の2つの式が得られる。
q * (X0 - 1) = A0 - B0 q * (X1 - 1) = A1 - B1
A0 - B0 と A1 - B1 のGCDからqを割り出すことができる。qがわかればpもわかり、あとは通常通りフラグを復号する。
#!/usr/bin/env python3 import socket from Crypto.Util.number import * def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('ctf.tcp1p.com', 13339)) data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + '-') s.sendall(b'-\n') data = recvuntil(s, b'\n').rstrip() print(data) A0 = int(data.split(' ')[-1]) data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + '%') s.sendall(b'%\n') data = recvuntil(s, b'\n').rstrip() print(data) B0 = int(data.split(' ')[-1]) data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'> ') print(data + 'p') s.sendall(b'p\n') data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + '-') s.sendall(b'-\n') data = recvuntil(s, b'\n').rstrip() print(data) A1 = int(data.split(' ')[-1]) data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + '%') s.sendall(b'%\n') data = recvuntil(s, b'\n').rstrip() print(data) B1 = int(data.split(' ')[-1]) q = GCD(A0 - B0, A1 - B1) for i in range(1024, 1, -1): if q % i == 0: q = q // i p = q + A1 data = recvuntil(s, b'> ') print(data + '3') s.sendall(b'3\n') data = recvuntil(s, b'\n').rstrip() print(data) c = int(data.split(' ')[-1]) n = p * q e = 65537 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) FLAG = long_to_bytes(m).decode() print(FLAG)
実行結果は以下の通り。
1. Get new prime 2. Get leak 3. Get flag 4. Exit > 2 choose leak p ? q (+-*/%) > - p - q = 115814447247898987174653674640002427826470623114321971756911451125879410357537069827768670261549665803102097351879975240475490537196175090499462994792264994964082363044469077031652802575559767656027591925053093297994010931779955284172192226148873840384040468604065081269943180302083240650682924298904311582930 1. Get new prime 2. Get leak 3. Get flag 4. Exit > 2 choose leak p ? q (+-*/%) > % p % q = 1697751797122058725071767231730176834582740336517746898279970323714148946762273424681991241011402635333103730631759019317912995246977129206696408974467452 1. Get new prime 2. Get leak 3. Get flag 4. Exit > 1 which prime? (p/q) > p 1. Get new prime 2. Get leak 3. Get flag 4. Exit > 2 choose leak p ? q (+-*/%) > - p - q = 169924164992005261237033280799176175439873104482134120038500949511709147837012495407350930846461335142813975896157445900113322258484757452047667069709724705859014067794972122743249066412943764762279587343921306378308508345964126999813129502895501205545533305901600203952150065997733457115657083259424971566384 1. Get new prime 2. Get leak 3. Get flag 4. Exit > 2 choose leak p ? q (+-*/%) > % p % q = 7896212417782321014076477527870303421927739781777317968566093872200742578599308920199432228517714303649559154449922360330194181582671937989154366832221627 1. Get new prime 2. Get leak 3. Get flag 4. Exit > 3 c = 195778194462868219673565885532046169896833887201775802395231173314108278325252744436171021700654426696737322956424678029953766966540784111631640041798037919046019957696873135214347206613573702744328179040292791682659440514393129882435115408306372771112443277747438218408405056730634378592565541078643582205699064281280042506523080998644996734296769338038651720972212336424990949087153781161251382935330602768190143244461673062605879432281343950050046391210986154 TCP1P{in_life's_abundance_a_fragment_suffices}
TCP1P{in_life's_abundance_a_fragment_suffices}
Open the Noor (Cryptography)
サーバの処理概要は以下の通り。
・CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ・KEY: ランダム16バイト文字列 ・systems = Systems() ・systems.adminpass = systems.gen_password() ・40バイトのランダムなCHARSETからなる文字列を返却 ・以下繰り返し ・choice: 入力 ・choiceが"1"の場合 ・userpass: 入力 ・check = systems.decryption(userpass) ・msg: userpassをhexデコード ・ivnya: msgの先頭16バイト ・msg: msgの先頭16バイト以降 ・msgをAES-CBC(KEY, ivnya)で復号し、アンパッドして返却 ※パディングが正しくない場合、Noneを返却 ・checkがNoneでない場合 ・checkが"nottheflagbutstillcrucialvalidation"の場合、フラグを表示 ・checkが"nottheflagbutstillcrucialvalidation"以外の場合、"[!] INTRUDER ALERT"と表示 ・checkがNoneの場合 ・"[!] Something's wrong."と表示 ・choiceが"2"の場合 ・systems.adminpass = systems.gen_password() ・choiceが"3"の場合 ・systems.secured_password()を表示 ・systems.adminpassをパディングし、AES-CBC暗号化 ・choiceが"4"の場合、終了
AES CBC Padding Oracle Attackで目的の平文の暗号文を作成する問題。
#!/usr/bin/env python3 import socket from Crypto.Util.Padding import pad from Crypto.Util.strxor import strxor def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def is_valid(s, msg): data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'] ') print(data + msg) s.sendall(msg.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if data == '[!] Something\'s wrong.': return False else: return True s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('ctf.tcp1p.com', 2223)) pt = b"nottheflagbutstillcrucialvalidation" pt = pad(pt, 16) pt_blocks = [pt[i:i+16] for i in range(0, len(pt), 16)] ct_blocks = [b'\x00'*16] * (len(pt_blocks) + 1) for i in range(len(pt_blocks), 0, -1): key = b'' for j in range(16): found = False for code in range(256): try_iv = b'X' * (15 - len(key)) + bytes([code]) + strxor(key, bytes([j+1])*j) ct = try_iv + ct_blocks[i] msg = ct.hex() if is_valid(s, msg): found = True key = bytes([code ^ (j+1)]) + key break assert found ct_blocks[i-1] = strxor(pt_blocks[i-1], key) msg = (b''.join(ct_blocks)).hex() data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'] ') print(data + msg) s.sendall(msg.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
You are connected to: ===================== The Sacred Noor ===================== 1. Login as Admin 2. Forgot password 3. Retrieve Encrypted Password 4. Exit > 1 Enter Admin Encrypted Password [?] 5858585858585858585858585858580000000000000000000000000000000000 [!] Something's wrong. 1. Login as Admin 2. Forgot password 3. Retrieve Encrypted Password 4. Exit > 1 Enter Admin Encrypted Password [?] 5858585858585858585858585858580100000000000000000000000000000000 [!] Something's wrong. : : 1. Login as Admin 2. Forgot password 3. Retrieve Encrypted Password 4. Exit > 1 Enter Admin Encrypted Password [?] 48b0817e3a28a4e5b7ccf54758b43dc414d84cec49fb37249852a6d066a8add4 [!] INTRUDER ALERT 1. Login as Admin 2. Forgot password 3. Retrieve Encrypted Password 4. Exit > 1 Enter Admin Encrypted Password [?] 36cfe51a425dd299c6bb87223cd759bd14d84cec49fb37249852a6d066a8add4421df3e819e8f79d033ab0818262448700000000000000000000000000000000 Logged In! Here's your flag: TCP1P{they_are_the_one_who_knocks}
TCP1P{they_are_the_one_who_knocks}