As the name suggests, the challenge 1 is a sanity-check to let players get familiar with Sui and the CTF framework.
From the code framework above, we can see that this challenge checks whether the player has successfully solved it by calling the `is_owner` function to check the `status` and evaluating the return value.
From the contract source code, it's evident that setting the `solved` field in `status` to `true` requires calling the `answer_to_life` function. This function demands the user to provide the `status` and a `u8` vector called answer. It then hashes the answer using `blake2b256` and compares it to a predefined hash. If they match, the `solved` field is set to true.
This hash is clearly irreversible, but based on the hint in the comments, "What is the answer to life?" we can deduce that the answer is 42, as found in The Hitchhiker's Guide to the Galaxy.
This challenge is similar to the previous one, as it also requires us to set the `solved` field of `status` to true. However, in this challenge, the function that can modify the `status` is `get_flag`. Due to the restrictions imposed by the `friend` mechanism, we can only invoke `get_flag` through the `prestige` function. To do so, users need to provide `ctxSender` with the value `0x31337690420`.
Since the contract doesn't perform any checks on the user input for `ctxSender` (which seems rather odd), all that is required is to provide the requested value.
This challenge presents an intriguing puzzle where the contract implements functions like `place_order` and `deliver_order` to simulate restaurant ordering and serving. A hamburger here can consist of five ingredients: Mayo, Lettuce, Chicken Schnitzel, Cheese, and Bun.
What makes it interesting is that when a user places an order for a hamburger, they provide a serialized byte sequence representing the hamburger. As chefs responsible for serving the orders, we need to deserialize the customer's order to obtain the recipe for the hamburger they desire.
In the context of BCS encoding, there's an interesting piece of trivia to note: the serialization result for a struct is essentially the serialization result of all its fields combined. In our scenario here, when it comes to serializing a burger, it means concatenating several u16 calorie values that make up its various components together.
Taking Order1 as an example, its serialization result has a length of 12. Therefore, we can attempt to deserialize it into six u16 numbers:
Based on the calorie settings for each ingredient in the contract source code, we can deduce the recipe for the first burger as follows: Bun, Mayo, Lettuce, Chicken Schnitzel, Cheese, Bun. Similarly, we can derive the recipe for the second one. Using the `ChefCapability` permission to "create" the individual components of the hamburgers, we can represent both hamburgers using two wrapper structs and then proceed with the delivery of the orders.
In this challenge, the author has created a coin flipping game that requires users to consecutively guess correctly 12 times in a row. It's worth noting that the randomness of the coin flips is not provided through VRF (Verifiable Random Function) but is generated using a custom-defined LCG (Linear Congruential Generator) to produce random numbers.
If we carefully examine the code for creating the game within the framework, we can observe that the seed for the LCG is actually just a u8. Therefore, we can potentially predict the outcome of the coin flips by brute-forcing this seed, with a 1/256 chance of guessing the correct seed.
Is there a more elegant way to obtain the seed without resorting to brute-force cracking? The answer is yes. Although Move language restricts us from accessing the struct's fields defined in the Foo module within the Bar module under normal circumstances, there's a clever workaround, which involves using the BCS encoding.
Recalling what we mentioned earlier, BCS encoding only serializes and concatenates the underlying types within a struct. In the case of the `Game`, after serializing it, if we read the ninth-to-last byte (skipping the 1 byte for `solved` and the 7 high-order bytes for `seed`), we will obtain the least significant byte of `game.randomness.seed`. Once we have this seed, we can use the same LCG as in the challenge to generate random numbers and predict the outcomes, achieving a 100% correct guessing rate.
The 2023 MetaTrust Web3 Security CTF offered a great set of challenges that tested our skills in Sui Move.
From the basic "Hello World" sanity check to the intricate mechanics of "Coin Flip," each challenge provided a unique learning experience. We hope this walk-through serves as a valuable resource for those looking to hone their skills in smart contract security.