Yogosha Christmas Challenge 2021 Writeup

Pri4ce
10 min readDec 31, 2021

This Christmas I was looking for something to cheer up my mind and got to know about this CTF, which felt interesting due to its stage-based unlocking of challenges. There was a variety of challenges on categories like OSINT, WEB.

LvL #1: Welcome Christmas — OSINT

Konoha village in Naruto is also enjoying Christmas but I heard there is a possible coup d’etat from a clan there :/ ShisuiYogo is a hero trying to save his village and clan. He shared something interesting that can lead you \o/
Please make sure to read the rules
Best Of luck Everyone and Merry Christmas From Yogosha Team
Flag Format: Yogosha{L33t}

This was an OSINT type challenge and if we check out their rules page then we can able to see 2 social media they use, from the above description we can conclude there might be something related to ShisuiYogo, after searching at Twitter we see that there is an account with usernameShisuiYogo. If we see the latest post from this account:

hmm.. a popular website where we can post images, Let's search Uchiha Shisui’s Jutsu

Shisui of the Body Flicker — ah he meant about flickr.com. If we go there and search for ShisuiYogo we see there is an account with the same name and profile pic as twitter. Which had a photo posted, If we try to check its EXIF info present on the right side of the page, we can clearly see the flag and description for the next level of the challenge.

flag of LvL1 & Description for LvL 2

LvL #2: Uchiha Or Evil ? — WEB

I heard something important is stored in /secret.txt here: http://3.141.159.106 ; Maybe the akatsuki will help the Uchiha clan ?

On checking http://3.141.159.106/robots.txt we see there might be something useful at http://3.141.159.106/read.php.

By default it's restricting us from visiting the page with the following message:

Access Denied. Only Uchiha clan can access this

We can able to bypass it by using Uchiha as User-agent.On the page when we click the Sharingan button then we can able to see the source code of the read.php at bottom of the page. Now let's observe the source code of read.php:

By looking at the code, it appears something might be there in guinjutsu.php But When I was trying to visit the page, it gave nothing. Back to the above source code. what this basically doing is that first splitting the input send to read.php keeping ‘|’ as a delimiter/separator. so if we send input:

hashOfSomthing|first:second:anything

then ‘hashOfSomthing’ would get stored in $hash variable and ‘first:second:anything’ would get stored in $filename variable.

and if line 58’s condition gets passed then. it will again split the values of $filename keeping ‘:’ as delimiter and print the file’s source code if it's one of the following files: “read.php”, “index.php”, “guinjutsu.php”.

so somehow we need to print guinjutsu.php’s code. for that, we need to pass line 58’s condition along with having :guinjutsu.php at end of our input.

what line 58 is basically testing is:

whether sha256 of $SECRET.$filename (concatenated) is equal to $hash or not.

But we don’t really know $SECRET so we can’t really calculate the hash of $SECRET.$filename . But we know the hash of $SECRET.read.php (already given in the code). This is a case of Hash Length Extension Attack. By Abusing which we can append our input to the old input ($SECRET.read.php ) and get hash of new string without even knowing the previous input( $SECRET). To know more about how it works: Here is a nice article

Here is a script which would Bruteforce the length of $SECRET and try different payloads and print the guinjutsu.php If the payload gets accepted by the server:

After running the script & filtering only guinjutsu.php code, we get:

After examining this. I tried to first visit the domain with port 5000 and found nothing useful, then I remembered maybe we need to do something with the hint given earlier: /secret.txt . But nothing found at: http://uchiha.fuinjutsukeeper.tech:5000/secret.txt (by the way, I see the domain is pointing to the same IP as where guinjutsu.php was hosted) neither there was any directory traversal possible here. then I looked closely at the check function. It was looking like we can bypass it. Since for scheme it's just checking whether http is present in the scheme or not, we can put any illegal scheme having http in it somewhere that can bypass the check function but just give a warning at file_get_contents() function( invalid/not found wrapper ) and try to include rest of the part just as its including a local file. so I thought maybe secret.txt is there in the server locally.. which can be accessed by simply a few ../../ via file_get_contents().

The final payload which can be made would be:

curl -X POST http://3.141.159.106/guinjutsu.php -d ‘submit=1&api=sddddhttpdfdffd://uchiha.fuinjutsukeeper.tech:5000/../../../../../../../../../../&endpoint=secret.txt’

Which gives us the flag along with the wrapper warning and description of the next level.

<br />
<b>Warning</b>: file_get_contents(): Unable to find the wrapper &quot;sddddhttpdfdffd&quot; — did you forget to enable it when you configured PHP? in <b>/var/www/html/guinjutsu.php</b> on line <b>26</b><br />
Yogosha{Master_Of_ArbitraRy_ReAdiNg_JuTsu}
Someone calling himself madara said to Itachi to kill everyone, I’m not sure about this intel but if it’s right no one can beat Itachi except Shisui. Check this forum they are using http://3.141.109.49

LvL #3: Js and Uchiha Are Evils — WEB

Someone calling himself madara said to Itachi to kill everyone, I’m not sure about this intel but if it’s right no one can beat Itachi except Shisui. Check this forum they are using http://3.141.109.49

On browsing http://3.141.109.49/jutsu/0 (which was found from the home page of the given link). We get:

I heard that there is something interesting in jutsu number 1337, it’s the most secret one!!

On visiting http://3.141.109.49/jutsu/1337 we get an Access Denied Page. maybe somehow we need to bypass this..

On visiting http://3.141.109.49/jutsu/1 we get some interesting js code:

//I'm using the following to avoid access to jutsus higher than 9; is it safe? :let id = parseInt(request.params.id, 10);// baka saijin can't read the jutsus with id >9if (id > 9) {return res.render("jutsu", { jutsu: "Access Denied sorry" });}const jutsu = articles.at(id) ?? { jutsu: "Not found" };return res.render("jutsu", jutsu);

ah, this must be the code that is restricting us from visiting the page.

We can access elements even with negative numbers using .at method. For example, we can access 3 by 2 id and also by -3 id in [1,2,3,4,5]. I thought we can pass -8663 instead of 1337 (since we know the array had 10000 elements in total — from the challenge description) and this should do according to the above filter. but then Faced a new error: Hacking Attempted

Hmm looks like they are blocking the negative numbers too,

then I ran a few more tests like:

  1. tried ‘-abc’ which gave the same error which means it's checking whether ‘-’ is present in the input or not.
  2. tried ‘a-a’ does not give any error which means it's only checking the presence of ‘-’ at starting?
  3. From parseInt doc — Leading whitespace in the argument is ignored. Cool so we could just add whitespace before the negative number and it should let me in.. right? well-tried that also: ‘<whiteSpace>-8663’ which gave the same error. looks like they are also blocking the whitespace. (common)

Note: After a few days they also released the hint having the regex of blocking whitespace and ‘-’ :/^[\b\t\n\v\f\r \xa0]*-/ confirming above checks.

Also, I encounter this MDN page which states:

In JavaScriptThe ECMAScript Language Specification defines several Unicode codepoints as “white space”: U+0009 CHARACTER TABULATION <TAB>, U+000B LINE TABULATION <VT>, U+000C FORM FEED <FF>, U+0020 SPACE <SP>, U+00A0 NO-BREAK SPACE <NBSP>, U+FEFF ZERO WIDTH NO-BREAK SPACE <ZWNBSP>, and any other Unicode “Space_Separator” code points <USP>.

tried U+FEFF which was EF BB BF in utf-8 and it worked! Final link having the payload:

http://3.141.109.49/jutsu/%EF%BB%BF-8663

which gave:

Wow,Awesome Jutsu! It’s called Dockeru. I stored the jutsu code there: id=shisuiyogo pass=YogoShisuiIsStrong image=forum

Which are the credentials of a private docker repo. on downloading and examining the files, It appeared that it was the source code of the challenge we just bypassed. cool more Js 😅

by looking at the source, I thought our target might be to get the ssh credentials since it's the only one thing among the rest of the service details which was imported from the environment variable. for which we need to somehow bypass the login.

for bypassing login we can see line 90, the code is directly using user input for manipulating the URL which is responsible for getting us logged in (if it gets 200 status).

Which we can easily achieve by using ../? as username/user-input.

Ok after login we can now visit the services page where if we type service name like ssh, web, net then if it's present in the DB it would return ‘Service is UP’. but as we see at line 128, again we are trusting the user input too much. Here we can perform NOSQL injection, and since it only returning Service is Up or not which makes it a Blind one, where we can only get to know whether certain values exist or not. Here we can use some MongoDB regex just like we use LIKE in SQL, something like: ssh”,”IP”:{“$regex”: “^xyz.*”},”Service”:”ssh which would make the 128th line look something like:

{“Service”:”ssh”,”IP”:{“$regex”: “^xyz.*”},”Service”:”ssh”}

combining these things here is a code that would do the heavy lifting for us and fetch the SSH Credentials:

Which would get the following Credentials:

SSH IP: 52.2.9.67
SSH Username: shisuiedo
SSH Password: YogoshaxShisui

after this, I tried to log into the server but looks like they required a public key, I stuck here for some time 😅 after which when I tried the same credentials at 1337 port then it worked ( I guess the previous hint worked here too 😛 ).

While Using SSH details we got our 3rd Flag:

_ _ _ _ _ 
| | | | ___| |__ (_) |__ __ _
| | | |/ __| ‘_ \| | ‘_ \ / _` |
| |_| | (__| | | | | | | | (_| |
\___/ \___|_| |_|_|_| |_|\__,_|

Yogosha{Uchiha_SerVicE_To_Kill_DanzO}

LvL #4: Uchiha As A Service— BASH

After login, on ls -l we see there is a secret.txt, which is owned by user-privileged and on doing whoami looks like we don’t have permission to interact with it. on doing sudo -l :

Matching Defaults entries for user1 on 4ba07a237196:
mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
env_keep+=”HOSTNAME KAHLA HELL PHPRC HTTP SHELL
User user1 may run the following commands on 4ba07a237196:
(user-privileged) NOPASSWD: /usr/local/bin/php /dev/null

looks like we can run/usr/local/bin/php /dev/null as user-privileged. But /dev/null is making our task not easy, which is restricting us from running PHP code directly and also blocking the output. after spending some time I saw there something from the author itself 🙂 HOSTNAME KAHLA HELL PHPRC HTTP SHELL after which I searched about PHPRC, and found:

PHPRC environment variable is used when trying to find and load a configuration file php.ini. The next exploit for CVE-2019-11043 uses several PHP parameters to force arbitrary code execution.

Also found a POC related to that, on modifying for our case, here is the final payload:

export $’HOSTNAME=1;\nauto_prepend_file=/proc/self/environ\n;<?php echo shell_exec(“cat /home/user1/secret.txt”);echo "\n"; ?>’
export ‘PHPRC=/proc/self/environ’
sudo --user=user-privileged /usr/local/bin/php /dev/null

We get the following useful Output:

Flag=Yogosha{Uchiha_As_a_Service_Is_N0t_SecUr3}
Repo=https://github.com/shisuiYogo/killer
Token=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

LvL #5: Final Beast — WEB

Repo=https://github.com/shisuiYogo/killer
Token=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

After Downloading the files from the Private repo, we see that our final battle is gonna happen at 54.157.87.12 as usual, going through index.js:

by looking at the merge function the first thing that came into my mind.. prototype pollution, the code which was eye-catching here was from line 144.

since every object inherits from the Object type in JavaScript. This means that if you can pollute the Object type each JavaScript object of the environment is going to be polluted!

ok since there was no req.session.isAdmin & req.session.username set here, that means it will try to find them in its parent classes which we can pollute by:

__proto__[isAdmin]=LetmeIn&__proto__[username]=LetmeIn

but since the merge is filtering __proto__ out we need to come up with an alternative.. as we can see the merge is trimming the keys after check.. which means if we put spaces after __proto__ then it would get bypassed from the check and will get trimmed to __proto__ . so our payload for bypassing line 149 would be:

__proto__ [isAdmin]=LetmeIn&__proto__ [username]=LetmeIn

Note: actually if we use content type as application/x-www-form-urlencoded then the prototype pollution was really not necessary.

anyways next line which we had to bypass was line 149, unfortunately, due to lack of sufficient filters, this was easy to bypass via URL-encode which was unintended. the final payload would have been:

curl -X POST http://54.157.87.12/guinjutsu -H ‘Content-Type: application/x-www-form-urlencoded’ -d ‘filename=%252e%252e/flag.txt’

Revenge — WEB (Extra)

This is my revenge for Final Beast , Have fun solving it , if it's solvable ofc.. Flag is in /flag.txt
Link: http://54.157.87.12:1337
Congrats on completing all the previous challenges \o/

ok, so this one is the improved / fixed version of the Final Beast Challenge. here this time we can able to see the all necessary filters to avoid the unintended way (hopefully).

I was actually trying to think towards prototype pollution more than looking for other ways to bypass the filters until I saw the hint, which hinted towards querystring.unescape() method.

on looking more about the method I found:

By default, the querystring.unescape() method will attempt to use the JavaScript built-in decodeURIComponent() method to decode. If that fails then it falls back to unescapeBufferwhich uses Int8Array so it can be overflowed by using ‘N’(65326) which will be treated like: ‘.’ (46)

decodeURIComponent() can be failed/crashed by using some higher characters range with %, like: %lol

so final payload for the Revenge challenge could be:

curl -X POST http://54.157.87.12:1337/guinjutsu -d ‘__proto__ [isAdmin]=LetMeIn&__proto__ [username]=LetMeIn&filename=%lol/NN/NN/flagNtxt’

Output:

Yogosha{It_SeemS_Icouldn’T_Get_My_RevengE_After_All}

--

--