Disclaimer
Well, I wonder if this is really a tutorial... I haven't truly discovered the secret message -.-
It seems this challenge cannot be solved alone... the rest of the post explains why
Colors ? You mean characters
To start off, the challenge had this little image:
As you know, an image is represented as a list of pixels, each pixel being a point on the image.
Each pixel has 3 values: one for the amount of red color present in that point, another for the amount of green, and the last one for the amount of blue (so called RGB system).
For example, black is {0,0,0} - that is, no color at all - while pure red is {255, 0, 0} and white is {255, 255, 255}. Incidentally, each "color amount" ranges from 0 (minimum) to 255 (maximum), and that is why pure vibrant red is the way I said it is (a pixel like {120, 0, 0} is a not-so-bright red).
Well, the image we want has lots of pixels, but you can visually see that there are only 6 distinct colors. Extracting these colors, they are, in the RGB system (from left to right in the image):
{104, 109, 109}, {46, 116, 117}, {114, 98, 105}, {111, 46, 114}, {101, 112, 108}, {46, 99, 111}
Well, this has everything to do with alphanumeric characters ! As you know, a letter is just a number... in other words, a computer represents a letter by using a number (for example, letter "a" is 97, "b" is 98, ..).
Actually, not only letters, but every character is associated to a number (the hyphen "-" is 45, for instance). It gets somewhat confusing when the character is a digit... like, the digit "0" is represented by the number 48, "1" by 49, etc.
Note: if you are asking yourself why "a" is 97 and not another number, this is the ASCII character set (which defines which number corresponds to which character).
Now we can decode the message contained in the image (if we know ASCII, of course):
Looks like a URL, so I accessed it...
Well, on to the URL
This is what my browser displayed when I accessed:
const express = require('express'); const path = require('path') const app = express(); const flag = process.env.FLAG; app.get('/', (req, res) => { res.sendFile(path.resolve('index.js')); }); app.get('/secret', (req, res) => { const username = req.headers['x-replit-user-name']; if (!username) { res.send(`I don't know who you are :(`) return; } const index = username .split('') .reduce((a, v) => a + v.charCodeAt(), 0) res.send(`hey ${username}, ${flag[index % flag.length]}`) }); app.listen(3000);
Hum, looks like a webserver written in Node.js. I accessed the
/ route, so it returned me the contents of the
index.js file of the server.
But you see there is a route for
/secret... so, accessing it (), I got:
hey megatron0000, i
Well, not surprising, since the logic that defined this result is contained in the code of the server I just showed.
If you speak javascript, you will have noticed that the server is just summing up every character of your username (remember ? every character is associated to a number, so you can "add characters") and using the result to display only one character of the string variable
flag of the server (visible in the server code above). My username got me that letter
i on the end of my message
hey megatron0000, i
What is the flag ?
So, I want to know what is the full string
flag so badly that you see this tutorial here >_<
The thing is, since each unique repl.it username yields a single character of the
flag (mine was the letter
i), the only way to know what all the characters of the
flag are is to just have a lot of repl.it users access the same URL I did.
If you are curious, access it and tell everyone which letter you got... hopefully eventually we will have all the letters
big edit: Trying to forge a request to that URL with a custom
x-replit-user-name header (the request header through which the server knows your username) does not work ¬¬' Probably this header is created by the repl.it servers themselves based on your session data, so they know if you are who you affirm you are
Here what I've got so far:
After endless registrations, I figured out that the flag is mixed from 3 phrases:
"_itte11nd_"; "_turtte11_";"turbio#836". They seem to have a consistent pattern (each 10-char block will consist a part of the three constructing phrases up above).
Not really sure the two other phrases' meaning. I already tried to decode these phrase with Ceasar cipher related to the number 836 and test if it is login details to this site but got no luck, and the most reasonable things I got is "Say it and turtle"? The turbio#836 seems like it the 836th repls of Mason Clayton, who got his page as turb.io and he is an engineer of repl.it
With the color code, there is a page that does it for you real quick:
@aedunmore I found the problem. Its can either 1 or l, because you can't really figure the different between 1 and l with the text formatting in the website xD.
Anyway, I found the message. But I don't know what to do with it haha. You just helped me a lot.
#Edit: I forgot to leave the message here. I thought there is no way to find out the flag instead of repetitive try with a different username (you can create one really easy without having a real email).
So to save your time, I will leave it here. Don't open it if you want to have the fun for yourself.
@megatron0000 I noticed the remainder operation. But when tested against the formula flag[index % flag.length], single time string was not working properly. So in my opinion, it randomly starts and ends somewhere between the string and we are unable to test because of the registration's restriction.
#Edit: And as any repetitive string with the remainder operation, the real flag can always be hidden when the test is restricted, because of the natural repeating pattern of itself. You can never be sure unless you know its exact length!
P/s: To be more clear, I mean flag = "123456" and flag = "123456123456" produce the same result under the remainder formula.
Edit #2: You make me realized that we can derive the minimum block of the flag. And I made a change! Thanks a lot, mate xD
Here's what I get:
hey Yusamac205, _
Wow, you guys got a lot more out of this then I did, all I thought when I saw the image was, "Oh, this would make a cool loading screen".
I also saw a fun illusion I could create.
It looks like the session data that hmm.turbio.repl.co/secret accesses to determine x-replit-user-name is stored in a cookie called
REPL_AUTH, and that the cookie is populated upon connection with a repl.it page.
REPL_AUTH is the only cookie accessed by hmm.turbio.repl.co/secret. Upon clearing this cookie, I get the
!username message until I connect to a repl.it page in my browser.
The cookie appears to be a standard ES256-encoded json web token (JWT).
Decoding it from Base64 yields a file that looks like this (possibly confidential info removed):
{"alg":"ES256","typ":"JWT"} {"name":"aedunmore","sub":"#######","roles":"my_roles","iat":##########}s٤iG`bډ*㞇8S#}HPYXϡ<
The first json is the header (encodes algorithm & token type), and the second is the payload (what we want to forge to pass to the web server.)
The third part is the signature and isn't Base64 encoded. It's generated by the ES256 algorithm as a function of the first two parts, and a public and secret key. Therefore, we if we modify the payload/header, the old signature will be rendered invalid. And we can't generate a new, valid signature without knowing the public/private key pair.
Is this a dead end? It kind of feels like it.
I got
hey aedunmore, l
I've been investigating whether there's a way to programmatically check all of the values in the
flag. I've been experimenting with using python's requests.get() function to pass on different usernames in the headers argument.
However, when I try to connect to hmm.turbio.repl.co/secret, I just get the
!username text:
import requests URL = '' headers = {'x-replit-user-name':'aedunmore'} r = requests.get(URL, headers = headers) print(r.text)
Output:
I don't know who you are :(
I have investigated this behavior further, by modifying my own express web server (hosted on repl.co) to print whatever headers are passed to the req object.
It appears that when I connect to a repl.co site via this python library,
x-replit-user-name (and
x-replit-user-id and
x-replit-user-roles) are overridden and set to blank strings. However, I have been successfully able to pass on other header variables, such as
user-agent.
I'm wondering if these values get set based on information in cookies or javascript local storage. However, I'm already stretching my knowledge of web backend stuff in investigating this stuff. Perhaps I'll share here if I find out more.
@aedunmore See the edit to the original tutorial... I guess the header is populated server-side based on the contents of a cookie sent to the server along with the request.
I had tried to do the same using npm's request module, also unsuccessfully
@megatron0000 I have faith that no one would go to all the trouble of making the challenge just to hide such an uninteresting flag.
Furthermore, while it does seem possible to crowdsource a solution to the challenge (getting
/secret results from lots of different users), I can't imagine that would be the only way to solve it - that wouldn't be very interesting either.
Oof I can't believe I missed a puzzle... By the way, most of these might make more sense in the context of the repl.it discord (). And no, that's not a hint since I haven't even started, but "turbio" is a person on the discord server (and you can see the beginning of a discriminator). (This is for you @leson238 )
@a5rocks I found out discord last week when playing LoL (I mentioned in the repls) and try to pm turbio to tell him that I found it. But I cannot reach him and he won't reply to my @ in the public channel, so it seemed that the scavenger hunt ends here. I was hoping that he somehow turn his discord account to be a bot that will reply special things when we saying the code like "open sesame" xD