How to forge and encode session cookie - flask

I am doing penetration testing on a website (localhost) and would like to know how to encode dictionary into correct jwt (session cookie?) format. I am completely new to web hacking and pen testing.
I have been given the source code. The part of the flask application is as follows:
#blueprint.route('/control-robot', methods=['GET', 'POST'])
#login_required
def control_robot():
great_mission= NewMission()
form = forms.NewMissionForm(request.form)
form.bot.choices = [('1', '2')]
print("#########################################")
if form.validate_on_submit():
form.populate_obj(great_mission)
session['great_mission'] = pickle.dumps(great_mission)
session['return_url'] = url_for('base_blueprint.control_robot')
print("#97")
return redirect(url_for('base_blueprint.security_key_confirm'))
print(session)
if 'great_mission' in session:
great_mission_txt = session.pop('great_mission', None)
print(great_mission_txt)
print("inside #103")
if(session.pop('verified', 0) < datetime.datetime.now().timestamp()-5):
flash('missing security key authentication', 'danger')
elif great_mission_txt:
great_mission = pickle.loads(great_mission_txt)
return config('FLAG')
print("#109")
return render_template('sites/control-robot.html', form=form)
The sitemap is as follows: dashboard->control_robot
if I click on "control_robot" on dashboard, then it executes above code and displays the form to be filled.
Following is the output on flask localhost debugging session after clicking on "control_robot":
ImmutableMultiDict([])
#########################################
<SecureCookieSession {'_fresh': True, '_id': 'ac1fcdef2c723257845d917bde568828b61069af207d0677376e397b930a7060528608493f4c965cb3c06c07cdd2345ba49acc56efb1a695515d9c11ec3469ae', '_user_id': '1', 'csrf_token': '1f67b99fd698b9eec3858c818f5722713a31fc4a'}>
#109
127.0.0.1 - - [18/Nov/2021 14:04:59] "GET /control-robot HTTP/1.1" 200 -
Notice the form dictionary is blank as no form has been filled. I want to get to the flag. Notice the second if condition, which checks if 'great_mission' key is present in the session. My idea is to forge the session cookie by including "great_mission" key and send the get request like:
cs = s.get(url + "control-robot", cookies = {"session":"new cookie"})
I get session cookie in following format:
.eJwljktqBDEMRO_idRaWZf3mMo0tS8wQSKB7ZhVy93HIsgrq1fspR55x3cvteb7ioxyPVW5lOKSvyObSsJFop2UgcwWxatPJUNlGtiqrsggKB5pMwzqkcqWmXLUbZndj8ole2av4Wg07zdFtuBNHThhsRLD5DhCOfXOjbJHXFee_Dezo15nH8_szvv6K5P1ludh0WuyVkrqCJklrAjhw-_dRft9jlD5c.YZax6g.tS__qotCbmnLiJgutCjo5R-q3o0
I assume this is in jwt format, thus I used flask decode which gives me following output:
$ flask-unsign --decode --cookie '.eJwljktqBDEMRO_idRaWZf3mMo0tS8wQSKB7ZhVy93HIsgrq1fspR55x3cvteb7ioxyPVW5lOKSvyObSsJFop2UgcwWxatPJUNlGtiqrsggKB5pMwzqkcqWmXLUbZndj8ole2av4Wg07zdFtuBNHThhsRLD5DhCOfXOjbJHXFee_Dezo15nH8_szvv6K5P1ludh0WuyVkrqCJklrAjhw-_dRft9jlD5c.YZax6g.tS__qotCbmnLiJgutCjo5R-q3o0'
{'_fresh': True, '_id': 'ac1fcdef2c723257845d917bde568828b61069af207d0677376e397b930a7060528608493f4c965cb3c06c07cdd2345ba49acc56efb1a695515d9c11ec3469ae', '_user_id': '1', 'csrf_token': '1f67b99fd698b9eec3858c818f5722713a31fc4a'}
is there any way to encode dictionary in above format, assuming I have the secret key?
and am I doing this correctly, if no what is the correct way to achieve this?

Related

Storing the data in the console in a database and accessing it

I'm building a web app which accesses the location of the user when a particular button is pressed for this I'm using the HTML geolocation api.
Below is the location.js file:
`var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude +
"<br>Longitude: " + position.coords.longitude;
console.log(position.coords.latitude)
console.log(position.coords.longitude)
}
Below is the snippet of the HTML file:
<button onclick="getLocation()">HELP</button>
<p id="demo"></p>
<script src="../static/location.js"></script>
What I want to do is send this information ( i.e. longitude/latitude of the user ) to list of e-mails associated with that user but I don't know how to store this data and access this after the button is pressed.
It would be of great use if someone could get me started on how to save this data corresponding to the user and accessing it from the database.
If you want to store this info to a django DB, then if might be easier to do this in a django view. This could be a RedirectView that just redirects to the same view after the button is clicked.
I have previously used a downloaded DB of the GeoLite2-City.mmdb, which might not always be up to date, but is ok.
You can get the ip address of a request in django with the ipware library. Then convert it into a python IP object in IPy. You can then use the geoip library to get the information out of the DB.
Import the following libraries;
from ipware.ip import get_ip
from IPy import IP
import geoip2.database
Then your method to get the IPs would be something like
class MyRedirectView(RedirectView)
def get_redirect_url(self, request, *args, **kwargs):
## Write some code to handle the redirect url first ##
ip_address = get_ip(self.request)
"""Ensure that the IP address is a valid IP first"""
try:
IP(ip_address)
except Exception:
logger.exception("GEOIP2 error: ")
"""Then get the IP location"""
geo_path = settings.GEOIP_PATH
reader = geoip2.database.Reader(geo_path + '/GeoLite2-City.mmdb')
try:
response = reader.city(ip_address)
city = response.city.name
country = response.country.name
### Some code here to save to your DB
return super(MyRedirectView, self).get_redirect_url(*args, **kwargs)
If you need a much more accurate IP location service you could involce an API call to something like http://ip-api.com/. But then you would have to wait for this response before serving the next view.

How to solve the URL decoding problem in Django and nginx

My web app is deployed using nginx. I have view like below for the url /incoming/`.
def incoming_view(request):
incoming = request.GET["incoming"]
user = request.GET["user"]
...
When I hit my url /incoming/?incoming=hello&user=nkishore I am getting the response I need. but when I call this url using requests module with below code I am getting an error.
r = requests.get('http://localhost/incoming/?incoming=%s&user=%s'%("hello", "nkishore"))
print r.json()
I have checked the nginx logs and the request I got was /incoming/?incoming=hiu0026user=nkishore so in my view request.GET["user"] is failing to get user.
I am not getting what I am missing here, is it a problem with nginx or any other way to call in requests.
See Requests Docs for how to pass parameters, e.g.
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> print(r.url)
https://httpbin.org/get?key2=value2&key1=value1
Internally, Requests will likely escape the & ampersand with &. If you really want to do the URL manually, try as your URL string:
'http://localhost/incoming/?incoming=%s&user=%s'

Why does this request return an empty string?

I have the following function which makes a get request to a url.
def fetch_data(session = None):
s = session or requests.Session()
url = 'http://www.moldelectrica.md/utils/load4.php'
response = s.get(url)
print response.status_code
data = response.text
return data
I expect to get a string back in the form.
169,26,0,19,36,151,9,647,26,15,0,0,0,0,0,150,7,27,-11,-27,-101,-19,-32,-78,-58,0,962,866,96,0,50.02
But instead I get an empty unicode string. The status code returned is 200.
I've looked at the request headers but nothing in them suggests that any headers will require being set manually. Cookies are used but I think the session object should handle that.
Figured it out. As I said this url provides data for a display so it wouldn't normally be visited directly. Usually it would be requested by the display page and that page would provide a cookie.
So the solution is to make a request to the display url then reuse the session and make another request to the data url.

Missing form data in request

I have following code
class MyClass(restful.Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('myfile.html'),200,headers)
def post(self):
session['CONSUMER_KEY']=request.form.get('consumer_key')
session['CONSUMER_SECRET']=request.form.get('consumer_secret')
render_template('myfile.html')
api.add_resource(MyClass,"/mag/",endpoint="mag")
I have written following test:
def mytest(self):
content_type={"Content-Type": "application / x - www - form - urlencoded","Content-Disposition": "form-data"}
response = self.client.post(
api.url_for(MyClass), data = json.dumps({'consumer_key':'testconsumerkey',
'consumer_secret':'testconsumersecret'}),
headers=content_type
)
The issue is form data is blank and thats the values are not getting set in session. When i debug i see that request.data is populated but request.form is an empty dictionary. Can someone suggest how I can send form data in a post request from a test
EDIT: Environment details
Python 2.7, Flask web framework, self.client is . I am using flask.ext.testing
You seem to be confused as to what the expected format for the post body should be. Should it be JSON data (which is what you send in the test case), or should it be in the application/x-www-form-urlencoded format (which is what you claim to send in the test case, and what the endpoint will read)?
If you wish to receive JSON data, you'll need to change the endpoint to read the data from request.get_json(). You'll also need to use application/json as the Content-Type header in the test case.
If you wish to receive urlencoded post data, then just simplify the test case by removing the Content-Type header and the json.dumps. Just pass the data dict to the data argument.

Updating ESRI Rest services with post

I am atempting to post a new url to a service on ESRI (I own it) with a post using Requests. After printing the line post_url the JSON is updated as I want it however when I post it nothing happens despite getting a 200 status. Is the issue with the post or the find / replace?
json_url = "https://www.arcgis.com/sharing/rest/content/items/serviceID?&token=XXX"
update_url = "https://www.arcgis.com/sharing/rest/content/users/USERNAME/folder/items/ServiceNumber/update?"
get_json = requests.get(json_url)
load_json = str(get_json.json())
find = "findme"
replace = "replace"
post_url = load_json.replace(replace, find)
a = requests.post(update_url, data={"url": post_url, "token": "XXXX", "f":"json"})
print a.status_code
The issue is with the post
I changed the post to this:
requests.request("POST", update_url, data={"url": post_url, "token": token, "f":"json"})
update_url needs to be the API update endpoint:
https://www.arcgis.com/sharing/rest/content/users/USERNAME/FOLDER/items/Endpoint /update?"
post_url needs to be: "whatever you want" in my case It was a search and replace variable of the the existing URL in the JSON and update, because of a server migration.