本文主要介绍Move CTF共学营Week1 Task1的解题思路。第一次尝试CTF挑战,感觉还挺有趣味性,有种在用代码解密的快乐,同时也能精进对Move的理解
参与了HOH社区举办的Move CTF共学,本文主要分享Week1 Task1解题思路
ctf参与平台: https://platform.cyclens.tech/activity/1 Task1代码可见: https://github.com/hoh-zone/lets-ctf/tree/main/src/ctfbook/chapter_1/task1
成功调用get_flag
函数即为夺旗成功。每次get_flag
被调用后,Challenge.secret
都会重新生成。
这次的合约需要自己部署到testnet, 部署后得到
package ID 0xf52bc6a6689a664a96d4d21ee14d600d148a2e8ca56547b6585d43421cbd4147
challenge ID 0x2b2540a47c79a4782ae24c1a72b941652cf403c83d2095752f4bf37986e9b144
首先想到的是在合约内写一个test function
,使用debug::print
打印所需要的数值,最后复制到CLI中调用
本次定义的
github_id
为 734ba641-cf91-4ffe-820c-ffa1548d823f
Challenge.secret
为字符串+gtpX_<QUl\%Kt-9zM>1>uC6[y2A
//增加一个反斜杠\转义字符
let secrect = string::utf8(b"+gtpX_<QUl\\%Kt-9zM>1>uC6[y2A");
let github_id = string::utf8(b"734ba641-cf91-4ffe-820c-ffa1548d823f");
除了guess data
,其余值的获取都可以通过复制原题代码代入数值
let data = *string::as_bytes(&secret);
//计算score
let secret_hash = sha3_256(data);
let expected_score = (((*vector::borrow(&secret_hash, 0) as u64) << 24) |
((*vector::borrow(&secret_hash, 1) as u64) << 16) |
((*vector::borrow(&secret_hash, 2) as u64) << 8) |
(*vector::borrow(&secret_hash, 3) as u64));
//计算length,magic_number
let seed = vector::length(&data) *2 ;
let magic_number = expected_score % 1000 + seed;
//计算hash_input
let mut bcs_input = bcs::to_bytes(&secret);
vector::append(&mut bcs_input, *string::as_bytes(&github_id));
let expected_hash = sha3_256(bcs_input);
debug::print(&seed);
debug::print(&magic_number);
debug::print(&expected_hash);
debug::print(&expected_score);
打印后得到,
score = 1231502107
seed = length(data1 * 2) = 56
magic number = score % 1000 + seed = 163
hash_input = 0x82a70eaefdbf471c17181784e596cbf36060e2006c40a8e4d2e7be3b9bc896be
vector<u8>
为[130, 167, 14, 174, 253, 191, 71, 28, 23, 24, 23, 132, 229, 150, 203, 243, 96, 96, 226, 0, 108, 64, 168, 228, 210, 231, 190, 59, 155, 200, 150, 190]
guess_data
由代码可知guess_data
需要满足条件sha3_256(guess_data || secret)[0..2] == challenge.round_hash[0..2]
, 即二者分别被哈希后的前2两个字节必须相同。
打印data
和round_hash
的值得到相应的值,可知round_hash
前2个bytes为0x4967
debug::print(&data)
//0x2b677470585f3c51556c5c254b742d397a4d3e313e7543365b793241
debug::print(&sha3_256(b"+gtpX_<QUl\\%Kt-9zM>1>uC6[y2A"));
//0x49673b1b9d73da90a305e38c7f241a6c4e4fbd9d2a55f9d2575d73b7dc432259
也可以在链上查询ObjectChallenge.round_hash
可得
round_hash
前两位为[73,103] = 0x4967
再使用python枚举,代码如下
# secret = *string::as_bytes(&challenge.secret))`
secret = bytes.fromhex("2b677470585f3c51556c5c254b742d397a4d3e313e7543365b793241")
# target round_hash prefix
target_prefix = bytes.fromhex("4967")
def find_valid_guess():
for i in range(0, 2**16): #遍历次数
guess = i.to_bytes(4, byteorder='big') # 设置guess为4字节
guess_data = guess + secret
hash_result = hashlib.sha3_256(guess_data).digest()
if hash_result.startswith(target_prefix):
print("Found valid guess!")
print(list(guess))
return guess
print("No valid guess found in range.")
return None
得到guess data
= [0, 0, 76, 231]
使用sui client调用函数
sui client call --package 0xf52bc6a6689a664a96d4d21ee14d600d148a2e8ca56547b6585d43421cbd4147 --module challenge --function get_flag --args 1231502107 "[0, 0, 76, 231]" "[130, 167, 14, 174, 253, 191, 71, 28, 23, 24, 23, 132, 229, 150, 203, 243, 96, 96, 226, 0, 108, 64, 168, 228, 210, 231, 190, 59, 155, 200, 150, 190]" 734ba641-cf91-4ffe-820c-ffa1548d823f 163 56 0x2b2540a47c79a4782ae24c1a72b941652cf403c83d2095752f4bf37986e9b144 0x8
返回如下信息表示成功
填入表中得到真正的flag,再返回提交即可
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!