Closed 0utplay closed 1 year ago
In the code, Post
class is not instantiated
Yeah program does not work at all, completely unusable
any updates on this?
I don't have time nowadays but when I get a second i will download the latest TestFlight version and try to reverse engineer the API again😬
Ok thanks @notmarek 😬
@notmarek good to know, as I said some comments before, the problem seems to be with the Post class, I think the apis were not changed because I posted something with another website, I cannot give you more info since it happened some months ago.
Seems like i will be able to do it tomorrow since I've fallen sick.
Seems like they started cert pinning in the iOS app so I was unable to research anything. I might archive this project as i don't have access to an Android device or a jailbroken iOS device.
There has been a fix for uploading as well as the login captcha already on a fork
@notmarek In the past I was able to monitor requests using android studio emulator and this project: https://github.com/shroudedcode/apk-mitm But it didn't work the last time I used it, probably because of a stupid mistake I made, I currently don't have enough time to investigate...
@h7ndrx What fork is it?
They
Seems like they started cert pinning in the iOS app so I was unable to research anything. I might archive this project as i don't have access to an Android device or a jailbroken iOS device.
You can use AVD on android studio and root using https://github.com/newbit1/rootAVD and unpin the ssl then proxy straight to your computer.
The new post code was first found by hubertoschusch I believe in this playground.
Logging in has also been bypassed by Rvaidan, I believe he is using google cloud functions as a proxy generator or something like that. However when I use mitm to proxy login requests, its still dependent on different Certs from ios and android so Im quite unsure how he is bypassing the recaptcha.
Bereal is definitely stepping up
Heres my pythonic version of the posting that matches this project somewhat:
@app.route("/signedpostinstant/<token>/<uid>/", methods=["POST"])
@app.route("/signedpostinstant/<token>/<uid>/<caption>", methods=["POST"])
def signedpostinstant(token:str, uid:str, caption:str=''):
#==============================================================================================
print(request.form.to_dict())
print(request.files)
print(caption)
#==============================================================================================
ispublic = json.loads(request.form.to_dict()['public'].lower())
latitude = request.form.to_dict()['latitude']
longitude = request.form.to_dict()['longitude']
haslocation = json.loads(request.form.to_dict()['haslocation'].lower())
print(ispublic, type(ispublic))
print(latitude, type(latitude))
print(longitude, type(longitude))
print(haslocation, type(haslocation))
#file manipulation
def get_data(version):
version_data = io.BytesIO()
version.save(version_data, format="JPEG", quality=90)
version_data = version_data.getvalue()
return version_data
def extension(img):
mime_type = Image.MIME[img.format]
if mime_type != "image/jpeg":
if not img.mode == "RGB":
img = img.convert("RGB")
return img
p = request.files['primary']
primary = Image.open(io.BytesIO(p.read()))
primary = extension(primary)
prim_data = get_data(primary) #this is the primary image file
primarysize = str(len(prim_data))
s = request.files['secondary']
secondary = Image.open(io.BytesIO(s.read()))
secondary = extension(secondary)
sec_data = get_data(secondary) #this is the secondary image file
secondarysize = str(len(sec_data))
#==============================================================================================
apiurl = f"https://mobile.bereal.com/api/content/posts/upload-url?mimeType=image%2Fwebp"
headers = {
"authorization": "Bearer {}".format(token),
"accept-encoding": "gzip",
"user-agent": "okhttp/4.10.0",
"if-none-match": 'W/"507-M16WxEgA1LffRgMAGSRIlonfNV8"'
}
signed_upload_res = requests.get(url=apiurl, headers=headers)
print("----- SIGNED UPLOAD -----")
print(signed_upload_res)
print(signed_upload_res.json())
if signed_upload_res.status_code != 200: return signed_upload_res.json()
print('----- END -----')
signed_upload_res = signed_upload_res.json()
signed_upload_res = signed_upload_res["data"]
prim_path = signed_upload_res[0]["path"]
sec_path = signed_upload_res[1]["path"]
def intostorage(signed_res, file):
bucket = signed_res['bucket']
expires = signed_res['expireAt']
image_path = signed_res['path']
bucket_headers = signed_res['headers']
bucket_url = signed_res['url']
print("----- BUCKET INFO -----")
print(bucket, "\n >>>>")
print(expires, "\n >>>>")
print(image_path, "\n >>>>")
print(bucket_headers, "\n >>>>")
print(bucket_url, "\n >>>>")
print('----- END -----')
ret = requests.put(url=bucket_url, headers=bucket_headers, data=file)
print("----- BUCKET PUT RESP -----")
print(ret)
print(ret.text)
print('----- END -----')
if ret.status_code != 200: raise Exception(f"Error uploading image: {ret.status_code}, {ret.text}")
return ret
prim_bucket_ret = intostorage(signed_upload_res[0] ,prim_data)
sec_bucket_ret = intostorage(signed_upload_res[1] ,sec_data)
print("----- BUCKET RET -----")
print(prim_bucket_ret)
print(">>>>>")
print(sec_bucket_ret)
print('----- END -----')
now = pendulum.now()
taken_at = f"{now.to_date_string()}T{now.to_time_string()}Z"
payload = {
"isPublic": ispublic,
"isLate": False,
"retakeCounter": 0,
"takenAt": taken_at,
#"location": location,
"caption": caption,
"backCamera": {
"bucket": "storage.bere.al",
"height": 2000,
"width": 1500,
"path": prim_path,
},
"frontCamera": {
"bucket": "storage.bere.al",
"height": 2000,
"width": 1500,
"path": sec_path,
},
}
if haslocation: payload["location"] = {"latitude": latitude,"longitude": longitude,}
complete_res = requests.post(url=api_url+'/content/post',json=payload,headers={"content-type" : "application/json", "authorization": token},)
print(complete_res)
print(complete_res.json())
return complete_res.json()
Maybe throwing the BeReal APK into https://www.decompiler.com/ gives enough insight for reverse-engineering? I've made some good experiences with that approach.
It appears that BeReal stepped up their protection against reverse engineering, it now seems to detect HTTPToolkit on a rooted phone.
Using https://modules.lsposed.org/module/io.github.tehcneko.sslunpinning works and dumps the requests. I'll post them here tomorrow
Thank you @VxlerieUwU
Here are the post upload requests and responses. I forgot to update the app tho, I'll check tomorrow if there's any differences between version 0.61.7 and 0.63.4.
Individual photo upload
Headers
cache-control: public,max-age=172800
connection: Keep-Alive
content-length: 100862
content-type: image/webp
host: storage.googleapis.com
x-goog-content-length-range: 1024,1048576
PUT
https://storage.googleapis.com/storage.bere.al/Photos/userid/post/test-test.webp?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=prod-backend-fasterstore%40alexisbarreyat-bereal.iam.gserviceaccount.com%2F20230219%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20230219T215032Z&X-Goog-Expires=172801&X-Goog-SignedHeaders=cache-control%3Bcontent-type%3Bhost%3Bx-goog-content-length-range&X-Goog-Signature=signature
post upload, url https://mobile.bereal.com/api/content/posts
Headers:
accept-encoding: gzip
authorization: Bearer key
bereal-app-language: fr
bereal-app-version: 0.61.7
bereal-device-id: deviceid
bereal-device-language: fr
bereal-os-version: 8.1.0
bereal-platform: android
bereal-timezone: Europe/Paris
connection: Keep-Alive
content-length: 445
content-type: application/json; charset=utf-8
host: mobile.bereal.com
user-agent: okhttp/4.10.0
x-datadog-origin: rum
x-datadog-parent-id: 12345678910
x-datadog-sampled: 1
x-datadog-sampling-priority: 1
x-datadog-trace-id: 12345678910
POST content:
{
"frontCamera": {
"bucket": "storage.bere.al",
"path": "Photos/userid/post/test-test.webp",
"width": 1500,
"height": 2000
},
"backCamera": {
"bucket": "storage.bere.al",
"path": "Photos/userid/post/test2-test2.webp",
"width": 1500,
"height": 2000
},
"takenAt": "2023-02-19T21:28:43.367Z",
"isLate": true,
"visibility": [
"friends-of-friends"
],
"retakeCounter": 0,
"location": {
"longitude": xx,
"latitude": xx
}
}
Response:
{
"user": {
"id": "userid",
"username": "username",
"profilePicture": {
"height": 500,
"width": 500,
"url": "https://cdn.bereal.network/Photos/userid/profile/userid-unixtimestamp-profile-picture.jpg"
}
},
"primary": {
"height": 2000,
"width": 1500,
"url": "https://cdn.bereal.network/Photos/userid/post/picture1.webp"
},
"secondary": {
"height": 2000,
"width": 1500,
"url": "https://cdn.bereal.network/Photos/userid/post/picture2.webp"
},
"isLate": true,
"lateInSeconds": seconds late,
"moment": {
"id": "moment-id",
"region": "europe-west"
},
"id": "post id?",
"location": {
"longitude": xx,
"latitude": xx
},
"caption": null,
"retakeCounter": 0,
"comments": {
"sample": [],
"total": 0
},
"realmojis": {
"sample": [],
"total": 0
},
"screenshots": {
"sample": [],
"total": 0
},
"createdAt": "same date format as before",
"takenAt": "same date format as before",
"visibility": [
"friends",
"friends-of-friends"
],
"canDelete": true
}
Edit: I'm using a Nexus 5X running LineageOS 15.1 with latest magisk, zygisk LSPosed and the certificate unpinner above if you want to reproduce that.
Btw is anyone planning on creating a GTK4 frontend using this library? I just recently got a Pinephone Pro and having BeReal on it would be awesome 😃
@VxlerieUwU any idea of how to fix the login ? :)
I'll investigate and post some endpoints here soon this week. I think BeReal is making their platform much secure and harder to reverse engineer, we'll have to dump the api endpoints whenever a new update goes public.
I have some really bad news: the login process has gotten even more complicated, with the use of SafetyNet and other google stuff. Can someone with access to a jailbroken IOS device dump the login endpoints to see if they can be used by this library?
Uh I just tried logging in using the master branch and it just works?? Maybe someone using an Iphone can dump the newest user-agents and iOS receipt.
Edit: I've discovered the endpoint to generate the picture upload API. Gonna add it to my private fork and make a pull request soon.
I think I posted pythonic code for Signed picture uploads earlier if you would like to take a look
Btw the Picture class here needs to be completely rewritten, uploads are now for both primary and secondary image at the same time, and at a seperate endpoint for Realmojis.
When trying to post a BeReal using the
post
command an error occurs as the call to Post.create_post is missing a self.TypeError: create_post() missing 1 required positional argument: 'self'
The command I executed:
python3 BeFake.py post
Datafolder: