OverTheWire Advent Bonanza 2019 Challenge Zero Writeup

Here we go again. It’s been a while since I’ve posted a writeup (or done any CTFs for that matter) due to adulting responsibilities. I didn’t get to participate too much in last year’s OverTheWire Advent Bonanza which turned out to be quality. I have a bit more time this year so I figured I should try some of the challenges. OverTheWire’s Advent Bonanza 2019 Challenge Zero was released prior to Dec 1. I’m not sure what day they released it but I accessed it on Nov 30. Let’s jump in.

All that is given for this challenge is a link:

https://advent2019.overthewire.org/challenge-zero


Accessing this site in Google Chrome gives the following:

Accessing the link via Google Chrome

Not much to go off of here. Let’s take a look at the source via Inspect:

Inspecting via Google Chrome

Nothing all too exciting except for perhaps the “Hint” and the little bit that looks like:

1
<!-- browser detected: chrome -->

I might need that “Hint” bit later, but for now, the fact that it has a little comment saying what browser was detected, the server probably behaves differently based on my User-Agent string. Also, the text says Fox! Fox! Burning bright! which sounds like it is alluding to Firefox. So let’s try accessing the page via Firefox:

Accessing the link via Mozilla Firefox

Look at that! Different output when I use Firefox. For completeness, I also inspected via Firefox:

Inspecting via Mozilla Firefox

Nothing new buried in the source code there. However, the first line of the page (Did you know: Plain text goes best with a text browser.) is probably telling me to curl the page. So I went ahead and did that…

curl -X GET https://advent2019.overthewire.org/challenge-zero

Now this is definitely the right path. At first glance, it looks like the characters follow the base64 character set. I did an output redirection to a file:

1
curl -X GET https://advent2019.overthewire.org/challenge-zero > output

It also looks like the text has terminal colorization so if I open the output in Sublime Text, it looks like:

Colorized Output in Sublime Text

Not very text-processing-friendly. So let’s run this through sed and get an uncolorized version:

1
cat output | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g' > uncolor

Now our uncolorized version can be processed easily in Python. I wrote a quick line in Python to replace all of the unwanted characters and print out the text:

1
print open('uncolor', 'rb').read().replace('#', '').replace('\n', '').replace('\r', '')

This gives me the following output repeated many times:

1
2
3
4
5
6
7
8
9
10
YmVnaW4gNjQ0IGJvb3QuYmluCk1eQydgQ01CLlAoWzBPYCFcMGBeQiNSI2BAXiNbQFxAIiNSK2AjUiNAIzBgJiNSK0BPTzlcWi
tYYDlAXloKTVgxRVMzO1g4Pz5CUWArXGA/QydgUzE4XCM3MDovYEFVI1gnX2AnWV5bS1kmPz5CNmAkX0tZOkpUI0xUMApNWl1a
IV9RIV49PFwvKmA7UD8wXEgnQCFeWiMwYDlAX08nTiFdOUBcWCVdTVQiTk5TT0I1XVomMGBaX1peCk00J1QvKmA4YD9AXEgnLk
AvYGBcSScoLyYkKCdeWCdVVVo+Rk1gJjgvW10pRiNeXzhOXjRWTScvIVpgP1YKTVxYQEZPR1FGI1NLP1IkNUYjVyMpX1BfJlQh
IUYjXl8iQCM7Jz8pUVhcNjgvW1wkWF8nMCc5QFxYVy1DSwpNU0Y4Ly4tVzhQWzAuSyMxIj1gOFFWXFQwWl8vIzNUQDUpVihXLE
I0UChSOEcpRihELCJUTzhBYCE9RihWCk0qQkxROENMRyhTIUMwRF0oJEIsUSwzNE0sIjlYOEQpLzJgJE0rUj1CKCIsQSo2KFUq
UzhKOEItQitSVEYKTSlTYEw4QCQyJVYpWCREKSo4REkiRCkiMEQpIjBUKC9LQlFeIU9aLFAsITE5RVlIVSQhUSIwXjBeKz84PQ
pNTEdZWicsS18iND4nK05fVUsmPUEtRjsqJUNcJVw9PSgvM0tUYFosMlo5SCMiIichITheUiRANztdQi1YCk0sIUlCIV4wR15c
IktWSkE7QjNMWltVV0gqWiZOW1wxQz5CST4hKjpdPEsvSCUrMywiO1tCQD47RTcySVYKTT4kWiU/KlE0YEZfUSdCIktEV1guMk
JeWUBaOEYtMiQ4LzBNRFYnLl1dX1g6QCM5MS4pIkAtISVNLCVZMgoxNSZVWUArRkUiSTxEIzJXXC1AVjU1OkhgCmAKZW5kCg==

After decoding the base64, I got the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
begin 644 boot.bin
M^C'`CMB.P([0O`!\0`^B#R#`@^#[@\@"#R+`#R#@#0`&#R+@OO9\Z+X`9@^Z
MX1ES3;X8?>BQ`+\`?C'`S18\#70:/`AU#X'_`'Y^[KY&?>B6`$_KY:JT#LT0
MZ]Z!_Q!^=<\/*`;P?0\H'@!^Z#0`9@_O'N!]9@\X%]MT"NNSOB5]Z&0`Z_Z^
M4'T/*`8`?@\H'.@/``\I'(/&$('^X'UUZ>FM`&8/[])F#^_8N^4VM'/!Z`?V
M\X@FOGQF#SK?R$5F#W#)_P_&T!!F#^_"@#;'?)QX\68/[\$X_'0'9@\XW-CK
MSF8/.-W8P[0.K#1"=`8QV\T0Z_/#3T@5)V(W,B4P(R8G)F(D,"TO8A`!=F(V
M*BLQ8CLG(S!C0D]($B,Q,34M,"9X8D)/2`$M+R=B(",A*6(U*S8J8B-B+RTF
M)S`L8@$2%V)X$D)*8DI"D)"0D)"0T(/KBQ^!OZ,P,!19EYHU$!Q"0^0^+?8=
MLGYZ',K_"4>'+N_UK&=A-F;*%C\%\==(/3KT`Z,2Z9H#""'!!8^R$@7;]B-X
M,!IB!^0G^\"KVJA;B3LZ[UWH*Z&N[\1C>BI>!*:]<K/H%+3,";[B@>;E72IV
M>$Z%?*Q4`F_Q'B"KDWX.2B^Y@Z8F-2$8/0MDV'.]]_X:@#91.)"@-!%M,%Y2
15&UY@+FE"I<D#2W\-@V55:H`
`
end

This looks to be Uuencoding. Luckily, Python handles this elegantly as well:

1
2
3
4
uudecoded_text = uuencoded_text.decode('uu')

with open('boot.bin', 'wb') as f:
f.write(uudecoded_text)

Now I can check what this file is…

1
2
3
4
$ ls -l boot.bin
-rw-r--r-- 1 wellingtonlee staff 512 Nov 30 00:57 boot.bin
$ file boot.bin
boot.bin: DOS/MBR boot sector

After doing quite a bit of research into DOS/MBR boot sector, basically I figured this was a reversing challenge as the code starting at the beginning of the 512 byte file is executed and set at offset 0x7c00. A good first step is to disassemble the binary portions with objdump. Some special flags need to be used, however. The boot sector code uses 16 bit addressing and also 16 bit data segments so I had to specify this manually.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
$ objdump -M intel -D -b binary -mi386 -Maddr16,data16 boot.bin

boot.bin: file format binary


Disassembly of section .data:

00000000 <.data>:
0: fa cli
1: 31 c0 xor ax,ax
3: 8e d8 mov ds,ax
5: 8e c0 mov es,ax
7: 8e d0 mov ss,ax
9: bc 00 7c mov sp,0x7c00
c: 40 inc ax
d: 0f a2 cpuid
f: 0f 20 c0 mov eax,cr0
12: 83 e0 fb and ax,0xfffb
15: 83 c8 02 or ax,0x2
18: 0f 22 c0 mov cr0,eax
1b: 0f 20 e0 mov eax,cr4
1e: 0d 00 06 or ax,0x600
21: 0f 22 e0 mov cr4,eax
24: be f6 7c mov si,0x7cf6
27: e8 be 00 call 0xe8
2a: 66 0f ba e1 19 bt ecx,0x19
2f: 73 4d jae 0x7e
31: be 18 7d mov si,0x7d18
34: e8 b1 00 call 0xe8
37: bf 00 7e mov di,0x7e00
3a: 31 c0 xor ax,ax
3c: cd 16 int 0x16
3e: 3c 0d cmp al,0xd
40: 74 1a je 0x5c
42: 3c 08 cmp al,0x8
44: 75 0f jne 0x55
46: 81 ff 00 7e cmp di,0x7e00
4a: 7e ee jle 0x3a
4c: be 46 7d mov si,0x7d46
4f: e8 96 00 call 0xe8
52: 4f dec di
53: eb e5 jmp 0x3a
55: aa stos BYTE PTR es:[di],al
56: b4 0e mov ah,0xe
58: cd 10 int 0x10
5a: eb de jmp 0x3a
5c: 81 ff 10 7e cmp di,0x7e10
60: 75 cf jne 0x31
62: 0f 28 06 f0 7d movaps xmm0,XMMWORD PTR ds:0x7df0
67: 0f 28 1e 00 7e movaps xmm3,XMMWORD PTR ds:0x7e00
6c: e8 34 00 call 0xa3
6f: 66 0f ef 1e e0 7d pxor xmm3,XMMWORD PTR ds:0x7de0
75: 66 0f 38 17 db ptest xmm3,xmm3
7a: 74 0a je 0x86
7c: eb b3 jmp 0x31
7e: be 25 7d mov si,0x7d25
81: e8 64 00 call 0xe8
84: eb fe jmp 0x84
86: be 50 7d mov si,0x7d50
89: 0f 28 06 00 7e movaps xmm0,XMMWORD PTR ds:0x7e00
8e: 0f 28 1c movaps xmm3,XMMWORD PTR [si]
91: e8 0f 00 call 0xa3
94: 0f 29 1c movaps XMMWORD PTR [si],xmm3
97: 83 c6 10 add si,0x10
9a: 81 fe e0 7d cmp si,0x7de0
9e: 75 e9 jne 0x89
a0: e9 ad 00 jmp 0x150
a3: 66 0f ef d2 pxor xmm2,xmm2
a7: 66 0f ef d8 pxor xmm3,xmm0
ab: bb e5 36 mov bx,0x36e5
ae: b4 73 mov ah,0x73
b0: c1 e8 07 shr ax,0x7
b3: f6 f3 div bl
b5: 88 26 be 7c mov BYTE PTR ds:0x7cbe,ah
b9: 66 0f 3a df c8 45 aeskeygenassist xmm1,xmm0,0x45
bf: 66 0f 70 c9 ff pshufd xmm1,xmm1,0xff
c4: 0f c6 d0 10 shufps xmm2,xmm0,0x10
c8: 66 0f ef c2 pxor xmm0,xmm2
cc: 80 36 c7 7c 9c xor BYTE PTR ds:0x7cc7,0x9c
d1: 78 f1 js 0xc4
d3: 66 0f ef c1 pxor xmm0,xmm1
d7: 38 fc cmp ah,bh
d9: 74 07 je 0xe2
db: 66 0f 38 dc d8 aesenc xmm3,xmm0
e0: eb ce jmp 0xb0
e2: 66 0f 38 dd d8 aesenclast xmm3,xmm0
e7: c3 ret
e8: b4 0e mov ah,0xe
ea: ac lods al,BYTE PTR ds:[si]
eb: 34 42 xor al,0x42
ed: 74 06 je 0xf5
ef: 31 db xor bx,bx
f1: cd 10 int 0x10
f3: eb f3 jmp 0xe8
f5: c3 ret
f6: 4f dec di
f7: 48 dec ax
f8: 15 27 62 adc ax,0x6227
fb: 37 aaa
fc: 32 25 xor ah,BYTE PTR [di]
fe: 30 23 xor BYTE PTR [bp+di],ah
100: 26 27 es daa
102: 26 62 24 bound sp,DWORD PTR es:[si]
105: 30 2d xor BYTE PTR [di],ch
107: 2f das
108: 62 10 bound dx,DWORD PTR [bx+si]
10a: 01 76 62 add WORD PTR [bp+0x62],si
10d: 36 2a 2b sub ch,BYTE PTR ss:[bp+di]
110: 31 62 3b xor WORD PTR [bp+si+0x3b],sp
113: 27 daa
114: 23 30 and si,WORD PTR [bx+si]
116: 63 42 4f arpl WORD PTR [bp+si+0x4f],ax
119: 48 dec ax
11a: 12 23 adc ah,BYTE PTR [bp+di]
11c: 31 31 xor WORD PTR [bx+di],si
11e: 35 2d 30 xor ax,0x302d
121: 26 78 62 es js 0x186
124: 42 inc dx
125: 4f dec di
126: 48 dec ax
127: 01 2d add WORD PTR [di],bp
129: 2f das
12a: 27 daa
12b: 62 20 bound sp,DWORD PTR [bx+si]
12d: 23 21 and sp,WORD PTR [bx+di]
12f: 29 62 35 sub WORD PTR [bp+si+0x35],sp
132: 2b 36 2a 62 sub si,WORD PTR ds:0x622a
136: 23 62 2f and sp,WORD PTR [bp+si+0x2f]
139: 2d 26 27 sub ax,0x2726
13c: 30 2c xor BYTE PTR [si],ch
13e: 62 01 bound ax,DWORD PTR [bx+di]
140: 12 17 adc dl,BYTE PTR [bx]
142: 62 78 12 bound di,DWORD PTR [bx+si+0x12]
145: 42 inc dx
146: 4a dec dx
147: 62 4a 42 bound cx,DWORD PTR [bp+si+0x42]
14a: 90 nop
14b: 90 nop
14c: 90 nop
14d: 90 nop
14e: 90 nop
14f: 90 nop
150: d0 83 eb 8b rol BYTE PTR [bp+di-0x7415],1
154: 1f pop ds
155: 81 bf a3 30 30 14 cmp WORD PTR [bx+0x30a3],0x1430
15b: 59 pop cx
15c: 97 xchg di,ax
15d: 9a 35 10 1c 42 call 0x421c:0x1035
162: 43 inc bx
163: e4 3e in al,0x3e
165: 2d f6 1d sub ax,0x1df6
168: b2 7e mov dl,0x7e
16a: 7a 1c jp 0x188
16c: ca ff 09 retf 0x9ff
16f: 47 inc di
170: 87 2e ef f5 xchg WORD PTR ds:0xf5ef,bp
174: ac lods al,BYTE PTR ds:[si]
175: 67 61 addr32 popa
177: 36 66 ca 16 3f ss retf 0x3f16
17c: 05 f1 d7 add ax,0xd7f1
17f: 48 dec ax
180: 3d 3a f4 cmp ax,0xf43a
183: 03 a3 12 e9 add sp,WORD PTR [bp+di-0x16ee]
187: 9a 03 08 21 c1 call 0xc121:0x803
18c: 05 8f b2 add ax,0xb28f
18f: 12 05 adc al,BYTE PTR [di]
191: db f6 fcomi st,st(6)
193: 23 78 30 and di,WORD PTR [bx+si+0x30]
196: 1a 62 07 sbb ah,BYTE PTR [bp+si+0x7]
199: e4 27 in al,0x27
19b: fb sti
19c: c0 ab da a8 5b shr BYTE PTR [bp+di-0x5726],0x5b
1a1: 89 3b mov WORD PTR [bp+di],di
1a3: 3a ef cmp ch,bh
1a5: 5d pop bp
1a6: e8 2b a1 call 0xa2d4
1a9: ae scas al,BYTE PTR es:[di]
1aa: ef out dx,ax
1ab: c4 63 7a les sp,DWORD PTR [bp+di+0x7a]
1ae: 2a 5e 04 sub bl,BYTE PTR [bp+0x4]
1b1: a6 cmps BYTE PTR ds:[si],BYTE PTR es:[di]
1b2: bd 72 b3 mov bp,0xb372
1b5: e8 14 b4 call 0xb5cc
1b8: cc int3
1b9: 09 be e2 81 or WORD PTR [bp-0x7e1e],di
1bd: e6 e5 out 0xe5,al
1bf: 5d pop bp
1c0: 2a 76 78 sub dh,BYTE PTR [bp+0x78]
1c3: 4e dec si
1c4: 85 7c ac test WORD PTR [si-0x54],di
1c7: 54 push sp
1c8: 02 6f f1 add ch,BYTE PTR [bx-0xf]
1cb: 1e push ds
1cc: 20 ab 93 7e and BYTE PTR [bp+di+0x7e93],ch
1d0: 0e push cs
1d1: 4a dec dx
1d2: 2f das
1d3: b9 83 a6 mov cx,0xa683
1d6: 26 35 21 18 es xor ax,0x1821
1da: 3d 0b 64 cmp ax,0x640b
1dd: d8 73 bd fdiv DWORD PTR [bp+di-0x43]
1e0: f7 fe idiv si
1e2: 1a 80 36 51 sbb al,BYTE PTR [bx+si+0x5136]
1e6: 38 90 a0 34 cmp BYTE PTR [bx+si+0x34a0],dl
1ea: 11 6d 30 adc WORD PTR [di+0x30],bp
1ed: 5e pop si
1ee: 52 push dx
1ef: 54 push sp
1f0: 6d ins WORD PTR es:[di],dx
1f1: 79 80 jns 0x173
1f3: b9 a5 0a mov cx,0xaa5
1f6: 97 xchg di,ax
1f7: 24 0d and al,0xd
1f9: 2d fc 36 sub ax,0x36fc
1fc: 0d 95 55 or ax,0x5595
1ff: aa stos BYTE PTR es:[di],al

It’s important to note that not the entirety of this disassembly is actual code. I was sure that some of this was actually data. Up until about 0xf6, the instructions look more-or-less normal (by normal, I mean similar to other assembly code after my many, many hours of staring at assembly). Assembly code usually has a typical flow. By around 0xf6, the assembly looks a bit wonky. Later on, I found out that my assumption was correct - 0xf6 and beyond is data segments.

It always helps to be able to run a binary and see what it does. To do debugging on this binary, I used the Bochs IA32 Emulator. Installing the emulator with the dlxlinux demo gave me a project to go off of. After downloading and installing the Bochs Emulator, I followed instructions from The Starman’s page on “How to DEBUG System Code using The Bochs Emulator on a Windows PC”. I used the default bochsrc.bxrc file given with the dlxlinux demo but had to figure out how to use the custom DOS/MBR bootsector from the challenge. To do that, I took the hd10meg.img disk from the demo and overwrote the first 512 bytes with the boot.bin to create a final.img. This is likely fine, as I was pretty certain that the boot sector code I needed to run/debug wasn’t affected by the rest of the disk. It took me many hours of fiddling around and learning Bochs before I could figure all of this out.

Running Boot sector in Bochs: CPU too old?

Bochs has an option which allows you to specify what CPU model it uses. The likely scenario is that the default CPU that Bochs uses does not have SSE support for the xmm (and possibly the aes?) instructions I saw in the disassembly. I found a page from the Bochs User Manual which specifies the CPU models I can choose in the emulator. The default that Bochs uses was the bx_generic which does not seem to have SSE support. After trying several of them, I found that corei7_sandy_bridge_2600k worked fine. I modified the cpu line to the bochsrc.bxrc file in the appropriate location:

1
cpu: model=corei7_sandy_bridge_2600k, ips=15000000

Now running the emulator gives a different output:

Running Boot sector in Bochs: Crackme?

It looks to be a simple crackme? Before I tried to find the solution, something bugged me. There must be code grabbing the text for the program. Where were the texts “We upgraded from RC4 this year!” and “Password: “ coming from? I had a hunch that this text might have been encoded in the data portions in 0xf6 and onwards. Analyzing the assembly a bit more, I saw that there are several calls to 0xe8, which does some xor with 0x42 or B. Isolating these calls and the instructions immediately before them give:

1
2
3
4
5
6
7
8
9
10
11
  24: be f6 7c              mov    si,0x7cf6
27: e8 be 00 call 0xe8
...
31: be 18 7d mov si,0x7d18
34: e8 b1 00 call 0xe8
...
4c: be 46 7d mov si,0x7d46
4f: e8 96 00 call 0xe8
...
7e: be 25 7d mov si,0x7d25
81: e8 64 00 call 0xe8

It’s important to remember that the offset of the program is 0x7c00 for DOS/MBR bootsectors (Remember the hint from opening the webpage in Google Chrome? Hint: $ break *0x7c00). Since the entire boot.bin is only 512 bytes, I opted to just xor the entire thing with 0x42 to get the output and hexdump it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
00000000  b8 73 82 cc 9a cc 82 cc  92 fe 42 3e 02 4d e0 4d  |.s........B>.M.M|
00000010 62 82 c1 a2 b9 c1 8a 40 4d 60 82 4d 62 a2 4f 42 |b......@M`.Mb.OB|
00000020 44 4d 60 a2 fc b4 3e aa fc 42 24 4d f8 a3 5b 31 |DM`...>..B$M..[1|
00000030 0f fc 5a 3f aa f3 42 fd 42 3c 73 82 8f 54 7e 4f |..Z?..B.B<s..T~O|
00000040 36 58 7e 4a 37 4d c3 bd 42 3c 3c ac fc 04 3f aa |6X~J7M..B<<...?.|
00000050 d4 42 0d a9 a7 e8 f6 4c 8f 52 a9 9c c3 bd 52 3c |.B.....L.R....R<|
00000060 37 8d 4d 6a 44 b2 3f 4d 6a 5c 42 3c aa 76 42 24 |7.MjD.?Mj\B<.vB$|
00000070 4d ad 5c a2 3f 24 4d 7a 55 99 36 48 a9 f1 fc 67 |M.\.?$MzU.6H...g|
00000080 3f aa 26 42 a9 bc fc 12 3f 4d 6a 44 42 3c 4d 6a |?.&B....?MjDB<Mj|
00000090 5e aa 4d 42 4d 6b 5e c1 84 52 c3 bc a2 3f 37 ab |^.MBMk^..R...?7.|
000000a0 ab ef 42 24 4d ad 90 24 4d ad 9a f9 a7 74 f6 31 |..B$M..$M....t.1|
000000b0 83 aa 45 b4 b1 ca 64 fc 3e 24 4d 78 9d 8a 07 24 |..E...d.>$Mx...$|
000000c0 4d 32 8b bd 4d 84 92 52 24 4d ad 80 c2 74 85 3e |M2..M..R$M...t.>|
000000d0 de 3a b3 24 4d ad 83 7a be 36 45 24 4d 7a 9e 9a |.:.$M..z.6E$Mz..|
000000e0 a9 8c 24 4d 7a 9f 9a 81 f6 4c ee 76 00 36 44 73 |..$Mz....L.v.6Ds|
000000f0 99 8f 52 a9 b1 81 0d 0a 57 65 20 75 70 67 72 61 |..R.....We upgra|
00000100 64 65 64 20 66 72 6f 6d 20 52 43 34 20 74 68 69 |ded from RC4 thi|
00000110 73 20 79 65 61 72 21 00 0d 0a 50 61 73 73 77 6f |s year!...Passwo|
00000120 72 64 3a 20 00 0d 0a 43 6f 6d 65 20 62 61 63 6b |rd: ...Come back|
00000130 20 77 69 74 68 20 61 20 6d 6f 64 65 72 6e 20 43 | with a modern C|
00000140 50 55 20 3a 50 00 08 20 08 00 d2 d2 d2 d2 d2 d2 |PU :P.. ........|
00000150 92 c1 a9 c9 5d c3 fd e1 72 72 56 1b d5 d8 77 52 |....]...rrV...wR|
00000160 5e 00 01 a6 7c 6f b4 5f f0 3c 38 5e 88 bd 4b 05 |^...|o._.<8^..K.|
00000170 c5 6c ad b7 ee 25 23 74 24 88 54 7d 47 b3 95 0a |.l...%#t$.T}G...|
00000180 7f 78 b6 41 e1 50 ab d8 41 4a 63 83 47 cd f0 50 |.x.A.P..AJc.G..P|
00000190 47 99 b4 61 3a 72 58 20 45 a6 65 b9 82 e9 98 ea |G..a:rX E.e.....|
000001a0 19 cb 79 78 ad 1f aa 69 e3 ec ad 86 21 38 68 1c |..yx...i....!8h.|
000001b0 46 e4 ff 30 f1 aa 56 f6 8e 4b fc a0 c3 a4 a7 1f |F..0..V..K......|
000001c0 68 34 3a 0c c7 3e ee 16 40 2d b3 5c 62 e9 d1 3c |h4:..>..@-.\b..<|
000001d0 4c 08 6d fb c1 e4 64 77 63 5a 7f 49 26 9a 31 ff |L.m...dwcZ.I&.1.|
000001e0 b5 bc 58 c2 74 13 7a d2 e2 76 53 2f 72 1c 10 16 |..X.t.z..vS/r...|
000001f0 2f 3b c2 fb e7 48 d5 66 4f 6f be 74 4f d7 17 e8 |/;...H.fOo.tO...|

Here, it’s now easy to see that 0x7cf6 refers to the text We upgraded from RC4 this year!. 0x7d18 refers to the text Password:. 0x7d25 refers to the text Come back with a modern CPU :P. At the moment, the text referred to by 0x7d46 is a bit of an unknown.

After a bit more debugging, I found a length check for my input:

1
5c: 81 ff 10 7e           cmp    di,0x7f10

The above line of assembly always checks to see if the length of my input is 0x10 or 16 bytes long.

After many more hours of debugging the code with many breakpoints, I figured out that the routine is simply doing an AES ECB encryption with the 16 bytes at 0x7df0 as the key:

1
0x7df0: 6d 79 80 b9 a5 0a 97 24  0d 2d fc 36 0d 95 55 aa

And comparing the output against the 16 bytes at 0x7de0:

1
0x7de0: f7 fe 1a 80 36 51 38 90  a0 34 11 6d 30 5e 52 54

I wrote some quick Python code to decrypt the resulting comparison:

1
2
3
4
5
from Crypto.Cipher import AES 

key = '6d7980b9a50a97240d2dfc360d9555aa'.decode('hex')
aes = AES.new(key, AES.MODE_ECB)
print aes.decrypt('f7fe1a8036513890a034116d305e5254'.decode('hex'))

This gave me the output:

1
MiLiT4RyGr4d3MbR

I input this as the password to get the flag:

flag!

1
AOTW{31oct__1s__25dec}

See all the text that comes up after putting in the right password? This has to have been stored somewhere in the binary. After a bit more reversing, the data starting at 0x7d50 is AES encrypted with the password I input MiLiT4RyGr4d3MbR and then xor’d with 0x42. The code reuses the snippet of assembly from AES encrypting for this portion. I assume that this was possibly to keep the binary under 512 bytes (and fitting within a DOS/MBR bootsector). I wrote a small Python snippet to output this as well:

1
2
3
4
5
from Crypto.Util import strxor
from Crypto.Cipher import AES
aes = AES.new('MiLiT4RyGr4d3MbR', AES.MODE_ECB)
to_decrypt = open('boot.bin', 'rb').read()[0x150:] # Offset of 0x7d50
print strxor.strxor_c(aes.encrypt(to_decrypt), 0x42) # Note that we AES encrypt here, instead of decrypt

The resulting output from running the above after omitting unprintable characters:

1
2
3
4
5
6
Wow, 512 bytes is a lot of space.
Enjoy the rest of AOTW!

Anyway, here's your flag: AOTW{31oct__1s__25dec}

- Retr0idBBBBBB

The B‘s at the end were most likely hex 0x0 being xor’d with the 0x42 (since 0x42 is B in ASCII). The actual data that is xor’d with 0x42 starts at 0x7d58, despite the AES encryption starting at 0x7d50.


Pretty fun for a zero-th challenge! I’m excited to see what other challenges there are in the advent. I’m writing these writeups as I solve them but these writeups won’t be published until after the event (which I believe is after Dec 26, 2019, 12:00UTC). There will most likely not be a writeup for every challenge, since I’m sure I can’t solve all of them. Until next time!