Skip to content

Commit 24c4d66

Browse files
Merge pull request #103 from despawningbone/prairielearn
Added PrairieLearn workspaces vuln writeup
2 parents e685178 + 358b0b4 commit 24c4d66

File tree

7 files changed

+277
-18
lines changed

7 files changed

+277
-18
lines changed

_posts/2022-05-09-sdctf-magic3.md

+13-18
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ Don't forget to tick the `Signed jump table elements` - the data at `unk_6084` (
4444
Nice! It's now showing what all the branches are doing in the decompilation. Ignoring the C++ boilerplates, it looks like aside from case 3 which jumps to `fail()` instantly, all 6 of the rest of the cases set some integer values before calling `magic1`, repeating this process 5 times in total. After poking around for a while, we can already reason about what the program is doing on the high level:
4545
- Before main, a static initializer `_GLOBAL__sub_I__Z11magic_wordsB5cxx11` is run by `_libc_csu_init`, which sets `magic_word` to the string `ldr buf`.
4646
- Once in main, it first calls setup_magic(), which sets up `magic_array` with consecutive integers with `std::iota`, then issues multiple calls to `magic1` with quite a bit of integers passed to it.
47-
- It then accepts 1 line of input with no length limit that matches any character in `magic_word`, maps the characters to the cases (aside from ` `), and process it by calling `magic1` 5 times with the integer parameters set before each call.
47+
- It then accepts 1 line of input with no length limit that matches any character in `magic_word`, maps the characters to the cases (aside from the space), and process it by calling `magic1` 5 times with the integer parameters set before each call.
4848
- Finally, it calls `test_magic` which iterates over the `magic_array`, printing the flag after asserting that `magic_array` consists of only ascending consecutive integers, just like the original state right after `std::iota`.
4949

5050
The only piece of the *puzzle* left now is to figure out what `magic1` is doing. Turns out, it is also quite straightforward:
5151

5252
![magic1.png](/assets/images/sdctf2022/magic3/magic1.png)
5353

54-
It loops over the values passed to the function, utilizing the values as indexes to pairwise swap the elements in `magic_array`. Note that it loops the indexes from the bottom up when considering the order shown in the decompilation - this is due to how varargs are handled in C++, where it takes the values furthest from the stack pointer first. Considering how simple this entire program is, why is it called magic, and to the extent of a *cube* even?
54+
It loops over the values passed to the function, utilizing the values as indexes to pairwise swap the elements in `magic_array`. Note that it loops the indexes from the bottom up when considering the order shown in the decompilation - this is due to the call to `std::reverse`. Considering how simple this entire program is, why is it called magic, and to the extent of a *cube* even?
5555
<br><br>
5656

5757
## Magic-ematics
@@ -74,12 +74,13 @@ magic = [
7474
def pairwise(iterable):
7575
a, b = itertools.tee(iterable)
7676
next(b, None)
77-
return zip(a, b)
77+
return zip(a, b)
7878

7979
def swapall(arr, indexes):
8080
for i, j in pairwise(indexes[::-1]): #reverse the indexes as noted in the above section
8181
arr[i-1], arr[j-1] = arr[j-1], arr[i-1]
8282

83+
# accepts argument as input
8384
test = sys.argv[1] if len(sys.argv) > 1 else ''
8485

8586
def l(na):
@@ -131,7 +132,7 @@ for c in test:
131132

132133
print(", ".join([str(i+1) + ":" + str(v) for i,v in enumerate(magic)]))
133134
```
134-
With some help of a native debugger and some breakpoints before `test_magic`, we can verify that their output do indeed match. After staring at it for quite a while, it still didn't seem like there is any pattern that we can identify - the values all seem quite arbitrary. Guess we might as well write a brute force script to run in the background while we figure the logic out:
135+
With some help of a native debugger and some breakpoints before `test_magic`, we can verify that their outputs do indeed match. However, after staring at it for quite a while, it still didn't seem like there is any pattern that we can identify - the values all seem quite arbitrary. Guess we might as well write a brute force script to run in the background while we figure the logic out:
135136
```java
136137
import java.time.OffsetDateTime;
137138
import java.time.format.DateTimeFormatter;
@@ -146,22 +147,16 @@ public class DeMagic {
146147
private static ForkJoinPool ex = new ForkJoinPool(28);
147148

148149
private static int[] magic = {
149-
40, 25, 29, 46,
150-
27, 45, 33, 34,
151-
15, 38, 13, 3,
152-
12, 18, 11, 47,
153-
39, 1, 8, 19,
154-
28, 31, 30, 32,
155-
0, 20, 23, 35,
156-
14, 26, 17, 16,
157-
42, 4, 21, 43,
158-
9, 10, 41, 24,
159-
37, 44, 2, 36,
160-
6, 7, 22, 5
150+
40, 25, 29, 46, 27, 45, 33, 34,
151+
15, 38, 13, 3, 12, 18, 11, 47,
152+
39, 1, 8, 19, 28, 31, 30, 32,
153+
0, 20, 23, 35, 14, 26, 17, 16,
154+
42, 4, 21, 43, 9, 10, 41, 24,
155+
37, 44, 2, 36, 6, 7, 22, 5
161156
};
162157

163158
private static void swapall(int[] arr, int[] indexes) {
164-
for(int i = indexes.length - 1; i >= 0; i--) { //once again, reversed
159+
for(int i = indexes.length - 1; i >= 0; i--) {
165160
int temp = arr[indexes[i]-1];
166161
arr[indexes[i]-1] = arr[indexes[i+1]-1];
167162
arr[indexes[i+1]-1] = temp;
@@ -276,7 +271,7 @@ g = nx.Graph()
276271
def pairwise(iterable):
277272
a, b = itertools.tee(iterable)
278273
next(b, None)
279-
return zip(a, b)
274+
return zip(a, b)
280275

281276
#patchy way to get individual color codes based on which function called swapall
282277
ct = {'l': 'r', 'd': 'g', 'r': 'b', 'b': 'yellow', 'u': "purple", 'f': "black"}

0 commit comments

Comments
 (0)