Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signed request expected / All Accounts banned at creation #26

Closed
ExTBH opened this issue Apr 29, 2022 · 58 comments
Closed

Signed request expected / All Accounts banned at creation #26

ExTBH opened this issue Apr 29, 2022 · 58 comments

Comments

@ExTBH
Copy link

ExTBH commented Apr 29, 2022

Issue

Secret key got changed
...

Environment

If you're reporting a bug, please attach the output of the following commands:

7.35
b'rYKfoeKQOwMHtBNTrCgvXEvgJBdwAHeZJFVqsBYh'

@ExTBH
Copy link
Author

ExTBH commented May 5, 2022

new secret: SlkXamhFfBJmlCyNlEuoqgoRSIWMRvOEhbgHBPKs
version: 7.44

i've managed to get the new ones for IOS but all accounts get insta banned no matter what

edit: there seems to be a new Key in /v2/users which is ['apple_device_token'] which authenticate the App before creating an account, which change on each new account(Coule be Apple APN)

for future if anyone wants to update this and have a Jailbroken Iphone this will extract the keys and log them in SYS log

@Unbrick
Copy link
Member

Unbrick commented May 7, 2022

You might want to give this key a try, I just extracted it from the Android-Version 7.37.1:

bGXTsUrGsQXlBpRxXjTIRvMAlDIQbweoOJDDAJMP

Moreover, please remember to change the client type from ios_ to android_.

@ExTBH
Copy link
Author

ExTBH commented May 7, 2022

Thanks, tried it still accounts are banned, Unfortunately i don't have an android Phone neither a Windows machine to check the requests for any change, if u can check them and gonna try

for IOS there is a 1 time request when you open the app for the first time, one you open the app and click continue it gets called if it lets you set Age then your phone is verified with Apple, they couldve implemented the same with Android.

I will try to race the method on IOS to get more info and maybe a way to fake the token.

@Unbrick
Copy link
Member

Unbrick commented May 7, 2022

Maybe you want to try @springjools solutions from issue #19, as far as I recall he got it working back then. If I get to it I'll take a closer look tomorrow and intercept some of the traffic.

@springjools
Copy link
Contributor

Yes, but my accounts also get instabanned. But the first time you check for ban, it reports false. But when you immediately check again, it reports true.

@ExTBH
Copy link
Author

ExTBH commented May 24, 2022

I've found the Token its server to server check https://developer.apple.com/documentation/devicecheck?language=objc

idk if its possible to spoof it or trick it
could be wrong one

edit2: found the method and was able to generate

@ExTBH
Copy link
Author

ExTBH commented May 28, 2022

I've figured everything and its not good :(
Since Version 7.40(in my tests) and up Jodel team Introduced new way to ban accounts by using DCDevice Token.
This token is per-device per-developerAccount and provides two 1 bit fields bit1: 0, bit2: 0 and the developer can assign any value for them.

So if someone tries to make an account with the API, they first need to get a token from their iPhone and make a request with it,
if this new account got banned Jodel team have a copy of the token in their server(it doesn't expire) and they will update apple servers with something like this bit1: 1, bit2: 0, now when you try to make a new account on your phone or by the API, Jodel will receive a new token from you and send it to apple and they see bit1: 1 and the new account is banned before its made.

So you need to wait until Jodel servers update apple servers and switch it back to bit1: 0 after you ban has expired to use a new account.

If the token that got sent to Jodel is wrong apple will respond with HTTP Code 400 and this account also get banned.

@springjools
Copy link
Contributor

springjools commented May 28, 2022

Ok, I think it's difficult to circumvent a DCDevice token, since if I understand it correctly, it's a server to server thing. But I understand this only applies to ios. What about the procedure on android? Something similar there?

@Unbrick
Copy link
Member

Unbrick commented May 28, 2022

Just took a look into the application, there seems to be an alternative route to register iOS devices which do not support the device check:
image

@ExTBH
Copy link
Author

ExTBH commented May 28, 2022

I've set isSupported to False and it created a new account without DCDevice but i got 7-10 day ban, i think im banned by device_uid i'll try to spoof it

can i ask what decompiler you use, it looks alot better than Ghidra

edit: this device_uid token is weird it changes every time i register, It looks Base64 but when i decrypt it it looks like this ољ �L�знѓkr:іҐ¤zЙq not like a UUID.

@ExTBH
Copy link
Author

ExTBH commented May 28, 2022

I've spoofed the UUID before it gets hashed but still getting insta ban

if anyone wants to try build this with Theos, it'll randomize it when called

is there a chance that Jodel just bans all accounts as precaution?

@Unbrick
Copy link
Member

Unbrick commented May 29, 2022

I use IDA as a decompiler for Jodel.

The Apple reference of DeviceCheck says the application must check whether DeviceCheck is supported by the device before using it. Maybe older iPhones do not support DeviceCheck. If we can find an Jodel-Version for e.g. iPhone 6 (iOS 12 is the last supported version) we could try using this version and HMAC-Key to register a user. This might circumvent the DeviceCheck entirely.

@ExTBH
Copy link
Author

ExTBH commented May 29, 2022

to my testing, jodel allows accounts made with old version to work in the old version, but trying to create an account with older version fails and says Please update the app, if you know the last supported version number on iPhone 6 i will downgrade to it and see

@ExTBH
Copy link
Author

ExTBH commented May 29, 2022

Think i made a mistake, I've downloaded Version 7.10 and checked the http request there is the same DCDevice token, it's not new they using it for over a year now

@ExTBH
Copy link
Author

ExTBH commented Jun 18, 2022

random 3 AM idea might worth it, i noticed on the app that when opening a link to a post it wont open in the app if i got nextDNS enabled, thats because Branch.io is integrated in the app and nexDNS blocks all requests to it. googling Branch.io shows

Branch's attribution technology is designed specifically for the world where universal identifiers such as IDFA and GAID don’t exist.

what if the accounts get banned because of Branch's stuff as its requests aren't implemented here?

we should try to create an account with Branch's HTTPS requests and see if its banned.

when i spoofed UUID before it was for jodel HTTPS requests, but i didn't check if branch's is logging any tokens or anything

im going to sleep now i will follow with this when i wake up.

@Unbrick
Copy link
Member

Unbrick commented Jun 18, 2022

Might be definitely worth a try but I thing i tested this some time ago and it did not have any effect.

@ExTBH
Copy link
Author

ExTBH commented Jun 19, 2022

my basic analysis
when creating a new account after wiping the app data Branch makes 1 request to https://api2.branch.io/v1/install
most of it is tracking garbage except a couple.
we care about the token and good stuff

1st token apple_receipt : Apple Docs, looks useless .

2nd apple_attribution_token : Apple Docs, another useless one for ads.

3rd ios_vendor_id & hardware_id : Apple Docs, looks like the only interesting thing only here.
and it gives a response :

Numbers are changed.

{
  "session_id": "10134534657934677872",
  "link": "https://shared.jodel.com/a/key_live_jcESXFnKwxb53gafuWSRFijozto4BLB5?%24randomized_bundle_token=1066931657934513952",
  "data": "{\"+clicked_branch_link\":false,\"+is_first_session\":false}",
  "randomized_device_token": "1059087119560141413",
  "randomized_bundle_token": "1066931657934513952",
  "invoke_register_app": true
}

not very useful but they got 1 thing right, not my first session so im in the system and probably tagged banned if Jodel really integrates with them

@ExTBH
Copy link
Author

ExTBH commented Jul 16, 2022

Might be helpful
iPhone 6 iOS 12.5.5 last supported version is 4.139
https://github.com/ExTBH/bug-free-repo/blob/main/IPAs/Jodel_4.139_iOSGods.com.ipa

@Unbrick
Copy link
Member

Unbrick commented Jul 22, 2022

Thank you very much @ExTBH!

Just updated the keys to the 4.139 version, for me most of the tests are currently passing, you might want to give it a try!

@ExTBH
Copy link
Author

ExTBH commented Jul 22, 2022

Out of home now without my mac, will be back in 9 hours and test, if this works i might use it for another project that i can't fix because of DeviceCheck, thanks

@ExTBH
Copy link
Author

ExTBH commented Jul 22, 2022

accounts banned for me, what are your results?
edit: maybe the endpoints are different?
image

@springjools
Copy link
Contributor

I also updated with git pull and it resolves the signed request expected-error. But accounts are still blocked. You need to test it like this:

`Python 3.10.1 (tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

import jodel_api
lat, lng, city = 48.148434, 11.567867, "Munich"
j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city)
Creating new account.
Creating account with data {'location': {'country': 'DE', 'city': 'Munich', 'loc_coordinates': {'lat': 48.148434, 'lng': 11.567867}, 'loc_accuracy': 15.457}, 'device_uid': 'e9e0b5fd04b0852d374a2206c877612b7393f12084289536083ad46b2b4644ac', 'language': 'de-DE', 'client_id': 'cd871f92-a23f-4afc-8fff-51ff9dc9184e'}
status, response = j.get_user_config()
print(status)
200
blocked = response.get('user_blocked')
print (blocked)
True`

Moreover, I rememberthat previously the first call used to return False, and any second call to get the same information used to return True.

Also before there was a case when the status of the blocked-variable depended on the lat and lon-coordinates you fed to the function, ie I think geo-fence of some sort.

@Unbrick
Copy link
Member

Unbrick commented Jul 25, 2022

Not sure what is going on, just tested the requests with updated keys (iOS, 7.51) and the response looks good for me!

Key is as follows:

    secret = 'YEKawcOEwzigovvWEFkBVWPIsgHhnIFmfMtfjYLS'.encode('ascii')
    version = '7.51'

And the corresponding requests i performed are:

import json
import time

if __name__ == "__main__":
    import jodel_api

    lat, lng, city = 48.123456, 11.987654, "Munich"
    j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city)
    status, response = j.get_user_config()
    print("Blocked ", response.get('user_blocked'))
    time.sleep(1)
    print("Karma", j.get_karma())
    time.sleep(1)
    print("Blocked ", response.get('user_blocked'))

which lead to the following output:

Creating new account.
Creating account with data [...]
Blocked  False
Karma (200, {'karma': 100})
Blocked  False

Could you please verify this on your end? If it works i'll update the keys accordingly

@springjools
Copy link
Contributor

springjools commented Jul 25, 2022

Weird. I updated the secret and version in L30-31 in jodel_api, ran setup build and setup install --user. I get this:

PS > python .\test.py
Creating new account.
Creating account with data {'location': {'country': 'DE', 'city': 'Munich', 'loc_coordinates': {'lat': 48.123456, 'lng': 11.987654}, 'loc_accuracy': 15.457}, 'device_uid': 'b32937fd1e430b55090b264cc657f6d8a3f06e81de0eb3eacc86902b86a421db', 'language': 'de-DE', 'client_id': 'cd871f92-a23f-4afc-8fff-51ff9dc9184e'}
Blocked True
Karma (200, {'karma': 100})
Blocked True

test.py is copy paste of the code you posted

@Unbrick
Copy link
Member

Unbrick commented Jul 25, 2022

Try altering the account creation to the following line to ensure the correct secret and version are used:

    j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city, _secret='YEKawcOEwzigovvWEFkBVWPIsgHhnIFmfMtfjYLS', _version='7.51')

Additionally you might want to modify the coordinates to a random value, some bans seem to be based on coordinates.

@springjools
Copy link
Contributor

But we used the same coordinates...

@Unbrick
Copy link
Member

Unbrick commented Jul 25, 2022

If accounts with the same coordinates are created from different IP addresses, they might detect it and ban them. Therefore a randomisation of coordinates might be necessary.

@ExTBH
Copy link
Author

ExTBH commented Jul 25, 2022

for a minute i thought Jodel team disabled this shit :(

edit: @Unbrick could you make another account and check to make sure?

lat, lng, city, country = 26.4591484, 50.117690, "Saar", "BH"

j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city, country=country)
time.sleep(5)
status, response = j.get_user_config()
print("Blocked ", response.get('user_blocked'))
time.sleep(1)
print("Karma", j.get_karma())
time.sleep(1)
print("Blocked ", response.get('user_blocked'))
Creating new account.
Creating account with data {'location': {'country': 'BH', 'city': 'Saar', 'loc_coordinates': {'lat': 26.4591484, 'lng': 50.11769}, 'loc_accuracy': 15.457}, 'device_uid': 'b9c0f5eb867c835e0430ea4c2523afe06b2c141509bf8361ab0bf3b3fd771d80', 'language': 'bh-BH', 'client_id': 'cd871f92-a23f-4afc-8fff-51ff9dc9184e'}
Blocked  True
Karma (200, {'karma': 200})
Blocked  True

@Unbrick
Copy link
Member

Unbrick commented Jul 25, 2022

Just checked, the block seems to dependant on the IP address. You might want to try residential proxies, i tested now with two different, one got me blocked, the other one worked like a charm!

Edit: Pushed the current version state to master now.

@ExTBH
Copy link
Author

ExTBH commented Jul 25, 2022

i'll check with my cellular IP, something is not right..

Edit: My cellular looks to be banned too

Edit2: in this Lib API URL is set to "https://api.go-tellm.com/api{}" in the app its https://api.jodelapis.com/api{}
how would we know the first isn't blacklisted

@IsakNyberg
Copy link

For what it is worth, I have tried this with a vpn from several locations (changing coordinates, city and country input every time) and i am also banned every time i make an account. I've tried with both "https://api.go-tellm.com/api{}" and "https://api.jodelapis.com/api{}".

@ExTBH
Copy link
Author

ExTBH commented Jul 25, 2022

What about switching to Android version? Does Android implement something like DeviceCheck? +Android is bigger, so if there any its probably bypassed.

@Unbrick
Copy link
Member

Unbrick commented Jul 25, 2022

I'll perform more tests tomorrow regarding IP address and location. @ExTBH the Android version requires email verification which would require a mail server to verify the account.

@Unbrick
Copy link
Member

Unbrick commented Jul 26, 2022

Sadly i now can replicate your issue, account seems to be banned instantly.

@ExTBH
Copy link
Author

ExTBH commented Jul 26, 2022

Guess that was a bug on their servers..
I see 2 options to get this working, if anyone has anything please say

  1. We figure out how to bypass DeviceCheck which will take ALOT of time and probably wont bypass it
  2. We switch to Android version and be limited by google accounts mail server

@Unbrick
Copy link
Member

Unbrick commented Jul 27, 2022

Defeating DeviceCheck is no possible option from my point of view. Leaving us with the email verification.

I recorded the required requests and documented the (hopefully) complete process here.

Now in theory all it needs is a mail server which allows API access. I tested OpenTrashmail which seems to do just fine. All it takes now is plugging it all together.

@ExTBH
Copy link
Author

ExTBH commented Jul 27, 2022

i can think of one problem here, Google banning domains after it notices a bunch of accounts made with it.

2 AM for me now, i'll check with a public mail provider tomorrow since i don't have a VPS to work with.

if this work i've got an idea to solve iOS bans for good, basic idea is, MITM the IOS app calling the api, replace the requests to android stuff, get the responses, convert them to something the iOS app understand

i don't see CORS headers also, should be possible for me to make a web client for the app with this

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

Oki I've followed the steps above but haven't tested step 4, the rest works.

I tried https://www.mohmal.com/ this email service and made a script to make it in 2 steps, should be able to use a free mail with API and work also.

from typing import Union
import requests

#Used by all methods here
baseURL = "https://www.googleapis.com"
querystring = {"key":"AIzaSyDFUC30aJbUREs-vKefE6QmvoVL0qqOv60"}

headers = {
    "X-Android-Package": "com.tellm.android.app",
    "X-Android-Cert": "A4A8D4D7B09736A0F65596A868CC6FD620920FB0",
    "X-Client-Version": "Android/Fallback/X21000001/FirebaseCore-Android",
}


def requestLogin(email: str) -> Union[bool, str]:
    url = f"{baseURL}/identitytoolkit/v3/relyingparty/getOobConfirmationCode"

    

    payload = {
        "requestType": 6,
        "email": f"{email}",
        "androidInstallApp": True,
        "canHandleCodeInApp": True,
        "continueUrl": "https://jodel.com/app/magic-link-fallback",
        "androidPackageName": "com.tellm.android.app",
        "androidMinimumVersion": "5.116.0"
    }

    response = requests.post(url=url, json=payload, headers=headers, params=querystring)

    if response.json()['kind'] == 'identitytoolkit#GetOobConfirmationCodeResponse' and response.status_code == 200:
        return True
    return f"{response.status_code} | Failed to create account | {response.text}"


def extractOob(link: str) -> Union[bool, str]:
    try:
        url = requests.utils.unquote(link)
        start = 'oobCode='
        end = '&continueUrl'
        return(url[url.find(start)+len(start):url.rfind(end)])
    except:
        return False


def redeemOob(oobCode: str, email: str) -> Union[str, dict]:

    url = f"{baseURL}/identitytoolkit/v3/relyingparty/emailLinkSignin"

    payload = {
        "email": f"{email}",
        "oobCode": f"{oobCode}"
    }

    response = requests.post(url, json=payload, headers=headers, params=querystring)
    responseJsoned = response.json()

    if response.status_code == 200 and responseJsoned['kind'] == 'identitytoolkit#EmailLinkSigninResponse':
        return {
            'idToken': responseJsoned['idToken'],
            'refreshToken': responseJsoned['refreshToken'],
            'expiresIn': responseJsoned['expiresIn'],
            'localId': responseJsoned['localId'],
        }
    return f"{response.status_code} | Failed to redeem Oob | {response.text}"


def refreshTokens(refreshToken: str) -> Union[str, dict]:
    url = "https://securetoken.googleapis.com/v1/token"


    payload = {
        "grantType": "refresh_token",
        "refreshToken": f"{refreshToken}"
    }

    response = requests.post(url, json=payload, headers=headers, params=querystring)
    responseJsoned = response.json()

    if response.status_code == 200:
        return {
            'access_token': responseJsoned['access_token'],
            'expires_in': responseJsoned['expires_in'],
            'refresh_token': responseJsoned['refresh_token'],
            'user_id': responseJsoned['user_id'],
            'project_id': responseJsoned['project_id']
        }
    return f"{response.status_code} | Failed to refresh tokens | {response.text}"


email = '<Your@mail.com>'



if requestLogin(email):
    oob = extractOob(input("Paste URL from Email: "))
    if oob == False:
        print("\n\nCan't extract OobCode")
        exit()

    oobTokens = redeemOob(oob, email) 
    if type(oobTokens) != dict:
        print('\n\n' + oobTokens)
        exit()
    
    refreshedTokens = refreshTokens(oobTokens['refreshToken'])
    if type(refreshedTokens) != dict:
        print('\n\n' + refreshedTokens)
        exit()
    print(refreshedTokens)
    ```

@Unbrick
Copy link
Member

Unbrick commented Jul 28, 2022

@ExTBH please check out the android_mail_auth branch. For me the registration using email works like a charm:

Requested email verification for [...]
Obtained oob token [...] for email [...]
Creating new account.
Creating account with data {'firebase_uid': 'jtNECbcwmfPGgQVuyKVPpsW8UIE3', 'firebaseJWT': 'eyJhbGciO[...]
Blocked  False
Karma (200, {'karma': 100})
Posts [200, {"posts": [{"message": "Podcast recs? I'm in to true crime " [...]
Blocked  False

Edit: I used a good portion of your code for the verification, i hope you are fine with this :)

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

Out of home, will check when im back.

Im all in FOSS so yeah im ok

@Unbrick
Copy link
Member

Unbrick commented Jul 28, 2022

Quick update: Seems like they have a absolutely brilliant bot prevention system. Even though verifying by mail my accounts are now blocked. And quick code update, you can now either pass a mail address and mail fetch handler directly in the constructor or leave them out. Leaving them out prompts you for manual input.

The fetch_handler requires one parameter (the email address argument) and should return a text containig the oobCode. The code is extracted automatically afterwards.

Code example with handler:

import json
import time
import jodel_api
import requests


def email_fetcher(email_address):
    email = None
    while not email:
        response = requests.get(f"https://your.email.service/{email_address}").text
        if "oobCode" in response:
            return response
        else:
            time.sleep(1)
            

if __name__ == "__main__":
    lat, lng, city = 48.834875, 2.344962, "Paris"
    j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city, email_fetch=email_fetcher, email_address="test@mail.com")

Example without handler (interactive mode):

Python 3.9.7 (tags/v3.9.7:1016ef3, Aug 30 2021, 20:19:38) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from jodel_api import jodel_api
>>> lat, lng, city = 48.834875, 2.344962, "Paris"
>>> j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city)
No email address is given, please enter it manually: bojeme2191@logodez.com
No email fetch handler registered, using manual mode
Requested email verification for bojeme2191@logodez.com
Please enter the link found in the email: https://ae3ts.app.goo.gl/?link=https://tellm-android.firebaseapp.com/__/auth/action?apiKey%3DAIzaSyBC5AfciIsT15NSwrfhLhsLG5UtFisbeSA%26mode%3DsignIn%26oobCode%3D4W6H43itna23hqunAKTIvh6MdHrHoeJm1Oton9LRz0oAAAGCRn7CPg%26continueUrl%3Dhttps://jodel.com/app/magic-link-fallback%26lang%3Den&apn=com.tellm.android.app&amv=5.116.0
Obtained oob token 4W6H43itna23hqunAKTIvh6MdHrHoeJm1Oton9LRz0oAAAGCRn7CPg for email bojeme2191@logodez.com
Creating new account.
Creating account with data [...]
>>> status, response = j.get_user_config()
>>> print(response.get('user_blocked')) 
True

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

my attempt with the new branch

edit1: email seems to be sensitive to spaces, it needs to be stripped from spaces that comes after the .[tld] part
edit2; tried different temp mail, also banned

edit3: In here, client_id is "client_id": "81e8a76e-1e02-4d17-9ba0-8a7020261b26", however in the logs below 'client_id': 'cd871f92-a23f-4afc-8fff-51ff9dc9184e'.

I think API's are separated now, every platform has different keys, we should probably fill all the fields filled in the logged requests

lat, lng, city, country =  23.175, 50.1486, "Manama", "BH"

j = jodel_api.JodelAccount(lat=lat, lng=lng, city=city, country=country)
time.sleep(5)
response = j.get_user_config()
print("Blocked ", response.get('user_blocked'))
time.sleep(1)
print("Karma", j.get_karma())
time.sleep(1)
print("Blocked ", response.get('user_blocked'))
No email address is given, please enter it manually:26291aa179@catdogmail.live
No email fetch handler registered, using manual mode
Requested email verification for 26291aa179@catdogmail.live
Please enter the link found in the email:<link>
Obtained oob token 7s2ezDQlh1vE2PnK-1lusERUvO-U-K8-03XzbpKFnYMAAAGCRppLOg for email 26291aa179@catdogmail.live
Creating new account.
Creating account with data {'firebase_uid': 'jtNECbcwmfPGgQVuyKVPpsW8UIE3', 'firebaseJWT': '<JWT>', 'location': {'country': 'BH', 'city': 'Manama', 'loc_coordinates': {'lat': 23.175, 'lng': 50.1486}, 'loc_accuracy': 15.457}, 'device_uid': '<RandomString>', 'language': 'de-DE', 'client_id': 'cd871f92-a23f-4afc-8fff-51ff9dc9184e'}
Blocked  True
Karma (200, {'karma': 100})
Blocked  True

@Unbrick
Copy link
Member

Unbrick commented Jul 28, 2022

Your third edit really did the trick for me there! Using the correct client_id, the accounts do not seem to be banned anymore.

Just pushed the changes, does it work for you?

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

im installing with

pip3 install git+https://github.com/JodelRaccoons/jodel_api.git@android_mail_auth#egg=jodel-api

but it doesn't allow me to set the email now, using same code as before, it just creates an account by itself which is bananed

seems like im getting the main branch, it says ver 7.51 and the iOS client_id

@Unbrick
Copy link
Member

Unbrick commented Jul 28, 2022

I split the accounts into iOSJodelAccount and AndroidJodelAccount. The current default is iOSJodelAccount as it is more easy to use for read only access. Try to use the AndroidJodelAccount like mentioned in the branches readme

Edit: Codeexample

lat, lng, city = 48.834875, 2.344962, "Paris"
 j = jodel_api.AndroidJodelAccount(lat=lat, lng=lng, city=city)

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

works! i get False now, i'll test making a post and update

edit: endpoint might be different

print(j.create_post(message="Test The Jodel_API.", imgpath=None, b64img=None, color=None, ancestor=None, channel=""))

returns, with unbananed account

(477, {'error': 'error', 'metadata': None})

@Unbrick
Copy link
Member

Unbrick commented Jul 28, 2022

477 sounds to me like the request signing is somehow broken. I'll check that tomorrow and will try to verify the signing mechanism.

@ExTBH
Copy link
Author

ExTBH commented Jul 28, 2022

calling below works fine, don't think its signing, i dont own an Android to check

j.get_posts_recent(skip=0, limit=60, after=None, mine=False, hashtag=None, channel=None)

iOS post Request looks like the below
link: https://api.jodelapis.com/api/v3/posts?hasDrawing=false&hasHashtag=false

{
  "location" : {
    "loc_coordinates" : {
      "lat" : 26.213154187145772,
      "lng" : 50.557814349952793
    },
    "loc_accuracy" : 3639.623509412248,
    "country" : "BH",
    "city" : "Manama",
    "name" : "unknown"
  },
  "color" : "A867E9",
  "test" : false,
  "channel_id" : "5f8ebbb3fd37e500256f7a67",
  "to_home" : false,
  "section" : "Main",
  "promoted_link" : "",
  "message" : "..."
}

@Unbrick
Copy link
Member

Unbrick commented Jul 29, 2022

Somehow the HMAC calculation seems off. I tested the same requests in BurpSuite (using the HMAC calculator extension) and they work just fine. Even using BurpSuite as a proxy for the python_api works flawlessly. But i can't spot the difference between the two requests:

Signed by BurpSuite:

POST /api/v3/posts/?explorer=False HTTP/1.1
Host: api.jodelapis.com
Accept: */*
Connection: close
User-Agent: Jodel/8.0.1 Dalvik/2.1.0 (Linux; U; Android 16; Pixel 9 Build/AAAA.123456.789)
X-Location: 52.504062;13.386062
X-Timestamp: 2022-07-29T09:54:56Z
Content-Type: application/json; charset=UTF-8
Authorization: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
X-Api-Version: 0.2
X-Client-Type: android_8.0.1
Content-Length: 238
Accept-Encoding: gzip, deflate
X-Authorization: HMAC 50951DEC80310EA812391D5A82A568DAF72E0C73

{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}

which is signed using the following HMAC input parameters:

[+] Signing request to https://api.jodelapis.com:443/api/v3/posts/
	[+] URL parameters: 
	explorer=False
	[+] Timestamp: 2022-07-29T09:54:56Z
	[+] Access token: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
	[+] Location: 52.504062;13.386062
	[+] Constructed HMAC String: POST%api.jodelapis.com%443%/api/v3/posts/%88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a%52.504062;13.386062%2022-07-29T09:54:56Z%explorer%False%{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}
	[+] New header:
	    Host: api.jodelapis.com
	    Accept: */*
	    Connection: close
	    User-Agent: Jodel/8.0.1 Dalvik/2.1.0 (Linux; U; Android 16; Pixel 9 Build/AAAA.123456.789)
	    X-Location: 52.504062;13.386062
	    X-Timestamp: 2022-07-29T09:54:56Z
	    Content-Type: application/json; charset=UTF-8
	    Authorization: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
	    X-Api-Version: 0.2
	    X-Client-Type: android_8.0.1
	    Content-Length: 238
	    Accept-Encoding: gzip, deflate
	    X-Authorization: HMAC 50951DEC80310EA812391D5A82A568DAF72E0C73

which successfully creates a post. Performing the same request from python:

POST /api/v3/posts/?explorer=False HTTP/1.1
Host: api.jodelapis.com
User-Agent: python-requests / jodel_api 8.0.1 (https://github.com/JodelRaccoons/jodel_api/)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Authorization: Bearer 88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c
X-Location: 52.5041;13.3861
X-Client-Type: android_8.0.1
X-Api-Version: 0.2
X-Timestamp: 2022-07-29T09:59:39Z
X-Authorization: HMAC ADDE3B615AFE80B6B2610BEEED98DD138F123F5D
Content-Type: application/json; charset=UTF-8
Content-Length: 238

{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}

with the following input parameters:

HMAC String: POST%api.jodelapis.com%443%/api/v3/posts/%88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c%52.5041;13.3861%2022-07-29T09:59:39Z%explorer%false%{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}
Requesting https://api.jodelapis.com/api/v3/posts/
     Endpoint: /v3/posts/
     Payload: {'color': '8ABDB0', 'location': {'country': 'DE', 'city': 'Berlin', 'loc_coordinates': {'lat': 52.504062, 'lng': 13.386062}, 'loc_accuracy': 15.457}, 'ancestor': None, 'message': 'Hallo zusammen', 'channel_id': '5f8ebbb3fd37e500256f7a67'}
     Method: POST
     Headers: {'User-Agent': 'python-requests / jodel_api 8.0.1 (https://github.com/JodelRaccoons/jodel_api/)', 'Authorization': 'Bearer 88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c', 'X-Location': '52.5041;13.3861', 'X-Client-Type': 'android_8.0.1', 'X-Api-Version': '0.2', 'X-Timestamp': '2022-07-29T09:59:39Z', 'X-Authorization': 'HMAC ADDE3B615AFE80B6B2610BEEED98DD138F123F5D', 'Content-Type': 'application/json; charset=UTF-8', 'Accept-Encoding': 'gzip, deflate'}
     Parameters: {'explorer': False}

returns the 477 unknown.

@ExTBH
Copy link
Author

ExTBH commented Jul 29, 2022

can't think of anything for this as i don't know anything about HMAC

@Steverman
Copy link

Steverman commented Jul 29, 2022

Somehow the HMAC calculation seems off. I tested the same requests in BurpSuite (using the HMAC calculator extension) and they work just fine. Even using BurpSuite as a proxy for the python_api works flawlessly. But i can't spot the difference between the two requests:

Signed by BurpSuite:

POST /api/v3/posts/?explorer=False HTTP/1.1
Host: api.jodelapis.com
Accept: */*
Connection: close
User-Agent: Jodel/8.0.1 Dalvik/2.1.0 (Linux; U; Android 16; Pixel 9 Build/AAAA.123456.789)
X-Location: 52.504062;13.386062
X-Timestamp: 2022-07-29T09:54:56Z
Content-Type: application/json; charset=UTF-8
Authorization: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
X-Api-Version: 0.2
X-Client-Type: android_8.0.1
Content-Length: 238
Accept-Encoding: gzip, deflate
X-Authorization: HMAC 50951DEC80310EA812391D5A82A568DAF72E0C73

{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}

which is signed using the following HMAC input parameters:

[+] Signing request to https://api.jodelapis.com:443/api/v3/posts/
	[+] URL parameters: 
	explorer=False
	[+] Timestamp: 2022-07-29T09:54:56Z
	[+] Access token: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
	[+] Location: 52.504062;13.386062
	[+] Constructed HMAC String: POST%api.jodelapis.com%443%/api/v3/posts/%88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a%52.504062;13.386062%2022-07-29T09:54:56Z%explorer%False%{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}
	[+] New header:
	    Host: api.jodelapis.com
	    Accept: */*
	    Connection: close
	    User-Agent: Jodel/8.0.1 Dalvik/2.1.0 (Linux; U; Android 16; Pixel 9 Build/AAAA.123456.789)
	    X-Location: 52.504062;13.386062
	    X-Timestamp: 2022-07-29T09:54:56Z
	    Content-Type: application/json; charset=UTF-8
	    Authorization: Bearer 88495644-abe900e3-ab95baad-b3db-47a4-bee1-ba8e4092bc1a
	    X-Api-Version: 0.2
	    X-Client-Type: android_8.0.1
	    Content-Length: 238
	    Accept-Encoding: gzip, deflate
	    X-Authorization: HMAC 50951DEC80310EA812391D5A82A568DAF72E0C73

which successfully creates a post. Performing the same request from python:

POST /api/v3/posts/?explorer=False HTTP/1.1
Host: api.jodelapis.com
User-Agent: python-requests / jodel_api 8.0.1 (https://github.com/JodelRaccoons/jodel_api/)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Authorization: Bearer 88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c
X-Location: 52.5041;13.3861
X-Client-Type: android_8.0.1
X-Api-Version: 0.2
X-Timestamp: 2022-07-29T09:59:39Z
X-Authorization: HMAC ADDE3B615AFE80B6B2610BEEED98DD138F123F5D
Content-Type: application/json; charset=UTF-8
Content-Length: 238

{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}

with the following input parameters:

HMAC String: POST%api.jodelapis.com%443%/api/v3/posts/%88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c%52.5041;13.3861%2022-07-29T09:59:39Z%explorer%false%{"color": "8ABDB0", "location": {"country": "DE", "city": "Berlin", "loc_coordinates": {"lat": 52.504062, "lng": 13.386062}, "loc_accuracy": 15.457}, "ancestor": null, "message": "Hallo zusammen", "channel_id": "5f8ebbb3fd37e500256f7a67"}
Requesting https://api.jodelapis.com/api/v3/posts/
     Endpoint: /v3/posts/
     Payload: {'color': '8ABDB0', 'location': {'country': 'DE', 'city': 'Berlin', 'loc_coordinates': {'lat': 52.504062, 'lng': 13.386062}, 'loc_accuracy': 15.457}, 'ancestor': None, 'message': 'Hallo zusammen', 'channel_id': '5f8ebbb3fd37e500256f7a67'}
     Method: POST
     Headers: {'User-Agent': 'python-requests / jodel_api 8.0.1 (https://github.com/JodelRaccoons/jodel_api/)', 'Authorization': 'Bearer 88778679-920de650-805418a1-07b7-40ab-9452-d7fb26cf140c', 'X-Location': '52.5041;13.3861', 'X-Client-Type': 'android_8.0.1', 'X-Api-Version': '0.2', 'X-Timestamp': '2022-07-29T09:59:39Z', 'X-Authorization': 'HMAC ADDE3B615AFE80B6B2610BEEED98DD138F123F5D', 'Content-Type': 'application/json; charset=UTF-8', 'Accept-Encoding': 'gzip, deflate'}
     Parameters: {'explorer': False}

returns the 477 unknown.

Couple of things that caught my eye :

  • The parameter explorer=False is used in the request but in the HMAC string the value is written as false

  • Rounding is probably a mistake as X-Location no longer matches what's in the HMAC string

    headers['X-Location'] = '{0:.4f};{1:.4f}'.format(self.lat, self.lng)

  • This probably is unrelated but user-agent is hardcoded:

    headers = {'User-Agent': 'Jodel/ (iPhone; iOS 12.5.2; Scale/2.00)'.format(self.version)}

@springjools
Copy link
Contributor

springjools commented Jul 29, 2022

Try to use double quotes instead of single ones. I know in a previous project, that this was the issue. Also the format of string/number.

Also in this other project, I remeber there was some difference with how you supply the payload to requests. I think it was better to use json = parameter instead of data = parameter. See this: https://stackoverflow.com/questions/9733638/how-to-post-json-data-with-python-requests

@ExTBH
Copy link
Author

ExTBH commented Jul 29, 2022

Try to use double quotes instead of single ones. I know in a previous project, that this was the issue. Also the format of string/number.

the quotes make sense, i think i've worked in a file before i used single or double don't remember and it errored

@springjools
Copy link
Contributor

I think json doesn't support single quotes, or was it requests? Or I may be wrong.

@ExTBH
Copy link
Author

ExTBH commented Jul 29, 2022

Yeah json is sensitive, maybe jodel server can't parse it
image

@Unbrick
Copy link
Member

Unbrick commented Jul 29, 2022

I replicated two absolute identical requests...the one in BurpSuire still worked, the one in python did not. Turned out you need to handle the HMAC key passed into the constructor instead of just ignoring it.

Pushed a new version.

@ExTBH
Copy link
Author

ExTBH commented Jul 29, 2022

Looks good
rsz_181755242-0eec722f-21d3-46fc-9c24-4ad02f78d521

@Unbrick
Copy link
Member

Unbrick commented Jul 29, 2022

Sounds like we can close this issue for now 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants