Skip to content

Commit

Permalink
writeups: arcane runes, microbuns, superguesser
Browse files Browse the repository at this point in the history
  • Loading branch information
kgeorgiou committed May 12, 2024
1 parent 7c43b49 commit 1f53284
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 1 deletion.
Empty file.
41 changes: 41 additions & 0 deletions misc/superguesser/solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Superguesser

## Solution

### Part 1

```go
seed := time.Now().Unix()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 10
```

If we know the seed, we know the random number generated. The Go random number generator will produce the same sequence of numbers for the same seed every time.

The seed in this case is predictable since it's the current time in Unix seconds.

We can take note of the Unix second at the time we connected to the challenge and use that to seed our own Go program locally and print out the `randomNumber` generated.

We also have 10 tries so there's some flexibility to also check subsequent seconds in case of network delays.

### Part 2

```go
seed := time.Now().UnixMilli()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 50
```

There a thousand milliseconds in a second. Same as Part 1, we can keep note of the current Unix millisecond we connect to the challenge. Due to network delays we'll need the 50 attempts to try subsequent milliseconds. We'll probably also need to run the challenge a few times until we get a bit lucky.

Another approach is to simultaneously connect to the challenge server from 2 clients in the hopes of both clients connecting at the same millisecond. If we achieve that, then we have 50+50 attempts at the same number, so we can "guess" it using [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) (we only need 63 attempts for a 63-bit number.)

### Part 3

```go
seed := time.Now().UnixNano()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 100
```

There are a billion nanoseconds in a second. We could still try our luck by spamming attempts but 100 tries are more than enough to perform a [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) like a pro.
Empty file.
41 changes: 41 additions & 0 deletions misc/superguesser_2/solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Superguesser

## Solution

### Part 1

```go
seed := time.Now().Unix()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 10
```

If we know the seed, we know the random number generated. The Go random number generator will produce the same sequence of numbers for the same seed every time.

The seed in this case is predictable since it's the current time in Unix seconds.

We can take note of the Unix second at the time we connected to the challenge and use that to seed our own Go program locally and print out the `randomNumber` generated.

We also have 10 tries so there's some flexibility to also check subsequent seconds in case of network delays.

### Part 2

```go
seed := time.Now().UnixMilli()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 50
```

There a thousand milliseconds in a second. Same as Part 1, we can keep note of the current Unix millisecond we connect to the challenge. Due to network delays we'll need the 50 attempts to try subsequent milliseconds. We'll probably also need to run the challenge a few times until we get a bit lucky.

Another approach is to simultaneously connect to the challenge server from 2 clients in the hopes of both clients connecting at the same millisecond. If we achieve that, then we have 50+50 attempts at the same number, so we can "guess" it using [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) (we only need 63 attempts for a 63-bit number.)

### Part 3

```go
seed := time.Now().UnixNano()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 100
```

There are a billion nanoseconds in a second. We could still try our luck by spamming attempts but 100 tries are more than enough to perform a [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) like a pro.
Empty file.
41 changes: 41 additions & 0 deletions misc/superguesser_3/solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Superguesser

## Solution

### Part 1

```go
seed := time.Now().Unix()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 10
```

If we know the seed, we know the random number generated. The Go random number generator will produce the same sequence of numbers for the same seed every time.

The seed in this case is predictable since it's the current time in Unix seconds.

We can take note of the Unix second at the time we connected to the challenge and use that to seed our own Go program locally and print out the `randomNumber` generated.

We also have 10 tries so there's some flexibility to also check subsequent seconds in case of network delays.

### Part 2

```go
seed := time.Now().UnixMilli()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 50
```

There a thousand milliseconds in a second. Same as Part 1, we can keep note of the current Unix millisecond we connect to the challenge. Due to network delays we'll need the 50 attempts to try subsequent milliseconds. We'll probably also need to run the challenge a few times until we get a bit lucky.

Another approach is to simultaneously connect to the challenge server from 2 clients in the hopes of both clients connecting at the same millisecond. If we achieve that, then we have 50+50 attempts at the same number, so we can "guess" it using [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) (we only need 63 attempts for a 63-bit number.)

### Part 3

```go
seed := time.Now().UnixNano()
random := rand.New(rand.NewSource(seed))
randomNumber, maxTries = random.Int63(), 100
```

There are a billion nanoseconds in a second. We could still try our luck by spamming attempts but 100 tries are more than enough to perform a [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) like a pro.
8 changes: 7 additions & 1 deletion web/arcane-runes/solution/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ Can be found in the HTML source code or by clicking the tiny margin on the right

### Bypassing the IP Address allowlist

// TODO
The allowed IP addresses are all reserved as [TEST-NET](https://en.wikipedia.org/wiki/Reserved_IP_addresses) so getting one of those addresses shouldn't be feasible. However since the code looks for the client IP address in the `X-Forwarded-For` header, we can spoof it by adding any IP address we want e.g. `curl $CHALLENGE_URL -H 'X-Forwarded-For: 233.252.0.0'`

### Obfuscation

This is how the payload was obfuscated, the task here is to reverse the obfuscation.

We can do that in a few ways, as long as we identify that the obfuscated code is valid Javascript -- this is a Web challenge after all.

One approach would be to copy-paste the obfuscated code in a browser's dev console and step through it via the debugger. The debugger should desplay the readable Javascript code. We'll have to do this one more time for the obfuscated flag piece since it was obfuscated twice.

👉 [Flag Payload](https://aem1k.com/transliterate.js/#%7B%22alphabet%22%3A%22iIloOyYzZpPq%22%2C%22code%22%3A%22console.debug%28%5C%22CCSC%7Bmyst1cal_cosplAI_0r_wAIst3_of_tAIm3%7D%5C%22%29%22%7D)

```
Expand Down
Empty file removed web/microbuns/solution/.gitkeep
Empty file.
21 changes: 21 additions & 0 deletions web/microbuns/solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Microbuns

## Solution

This challenge is based on the Javascript quirk where `parseInt` will successfully parse a number in a string even though there are non-numeric characters following that number. For example, `parseInt('6pack')` will return `6`.

Inspired by the bug mentioned [here](https://www.youtube.com/watch?v=iFtcathflSw&t=5886s) -- use this as a "writeup" :)

Sample payload for the `:user_id` path paremeter: **2**%2F%2E%2E%2F**1** (2/../1) where 2 is your User ID and 1 is the Admin's User ID.

That payload will bypass this middleware check:

```javascript
if (req?.params?.user_id) {
if (parseInt(req.params.user_id) !== decoded.user.id) {
return res.status(403).redirect("/login");
}
}
```
This should return the private haikus of the Admin user, including the flag in one of them: `curl "$CHALLENGE_URL/user/2%2F%2E%2E%2F1/haikus/private"`

0 comments on commit 1f53284

Please sign in to comment.