Home Hacker Mentality Series: EV charging clusterfuck
Post
Cancel

Hacker Mentality Series: EV charging clusterfuck

Purpose of this series

No,

  • harm to anyone
  • unethical stuff
  • exfil of data
  • bypassing rate limits or hardcore scraping

We try to use hacker mentality anywhere possible. Win small things in life …

Problem

EV charging is totally a cluster fuck(well at least it is in Turkey). There are tons of companies offering charging services. All have their own applications and they are not connected by any means. One needs to keep track of all applications, register to all of them, check their charging plans and find empty slots and so on. It’s frustrating, boring and time consuming.

I lived in the TRNC ( Turkish Republic of Northern Cyprus ) for some time this year. TRNC is not a developed country so there weren’t many charges on the island. There existed no DC charging infrastructure as well so AC chargers were the only option. There were 2 charging service providers on the island. Both offered free charging which was actually pretty good. There were, unfortunately, only 2 stations near to the place I lived in. Both stations had only 1 cable/slot so as you can imagine finding empty slots was painful. I had to check the apps multiple times to see if they were available or not.

I wanted to have something that automatically checks the availability for me and sends a notification.

I wanted this project to take at max 2 hours so started playing with it.

Overview

With app we can see available stations,

image

App doesn’t have an english translation so I will try to translate important points.

Durum and Meşgul translates to Availability and Busy so as you can see the station I wanted to charge my car at was busy.

image

Setting up the local environment

I downloaded the APK of the application

image

I set up my burp proxy to listen on all interfaces

image

I had already installed burp certificate on the machine so I just skipped to the proxy step.

You can follow https://notes.morph3.blog/mobile to install burp certificates and possible mobile related notes.

1
~  λ adb shell settings put global http_proxy 10.90.14.251:8080

First run,

I started seeing some requests being sent.

image

When I clicked on the locate me button it was sending my coordinates as 0,0. This was not good

image

After some investigation I noticed this error message below in the logcat.

1
2
3
11-10 21:22:54.667  2874  2950 W GoogleApiManager: The service for com.google.android.gms.internal.location.zzay is not available: ConnectionResult{statusCode=SERVICE_INVALID, resolution=null, message=null}
11-10 21:22:54.667  2874  2874 W GooglePlayServicesUtil: com.ipitex.incharge requires the Google Play Store, but it is missing.
11-10 21:22:54.696  2874  2874 W GooglePlayServicesUtil: com.ipitex.incharge requires the Google Play Store, but it is missing.

Looked like google services was not working properly.

Could it be solved ? Yes ofc. However, I have only 2 hours at max for this project so let’s just take shortcuts.

I found my location using https://www.gps-coordinates.net/my-location. I set my location and the service returned every single available charger. The service returned pretty much everything I needed so that’s all we need for now.

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

"Services":[
    {
        "Address":"Gazimağusa  \/ KKTC",
        "Amenities":null,
        "City":null,
        "Country":null,
        "CurrentlyOpen":true,
        "DcAvailable":0,
        "District":null,
        "ImageUrl":"REDACTED",
        "Lat":REDACTED,
        "Lon":REDACTED,
        "MerchantID":REDACTED,
        "MerchantName":"CITY MALL AVM",
        "NearBy":null,
        "OperatorID":7,
        "OperatorType":2,
        "OtherOptions":null,
        "PayMethods":null,
        "Phone1":"REDACTED",
        "Phone2":null,
        "Photos":null,
        "ServiceList":[
            {
                "Charge":null,
                "ServiceID":REDACTED,
                "ServiceImgUrl":"REDACTED",
                "ServiceName":"Şarj",
                "ServicePrice":0.0000
            }
        ],
        "StationCode":null,
        "StationDescription":"AC (22kW)",
        "Status":{
            "ID":1,
            "Value":"Kullanılabilir"
            },
        "TimeZone":null,
        "Title":"REDACTED",
        "WorkingHours":null
        },

Let’s get into coding,

Sensitive information is removed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import json
import time
import requests

def check_availability(station_name):
    """
    Checks if the target is available
    """

    # coordinates are removed obviously
    post_data = {"ChargePointOperatorRID":7,"FromMap":True,"Lat":0,"Lon":0,"Range":100,"filters":None}
    service_url = "https://SERVICE_URL"

    response = requests.post(service_url, data=json.dumps(post_data), headers={"Content-Type":"application/json"})
    response_json = json.loads(response.text)

    # based on our location, this one will be one of the closest so we won't loop through all
    for station in response_json["Result"]["Services"]:
        if station["MerchantName"] == station_name:
            print(f'We found the station {station["MerchantName"]}')
            if station["Status"]["Value"] == "Kullanılabilir":
                print(f'We found an available slot with ID: {station["Status"]["ID"]}')
                return int(station["Status"]["ID"])
            print("No available slots")
            return 0


def notify_user(slot_id, station_name):
    data = f"Found an available slot !! {station_name} : {slot_id}"
    notify_url = "https://ntfy.sh/YOUR_NOTIFY_SH_SERVER"
    requests.post(notify_url, data=data)
    return

if __name__ == "__main__":

    while True:
        slot_id = check_availability("XXX")
        if slot_id > 0:
            notify_user(slot_id, "XXX")
            time.sleep(900) # there is an empty slot so now sleep for 10 minutes
            print("User notified, sleeping for 15 minutes ...")
        else:
            print("No available slots, sleeping for 5 minutes ...")
            time.sleep(310) # sleep for 5 minutes + 10 secs, no stupid rate limits

I copied the script to my server. I run and started waiting,

1
2
3
4
╭─root at boom in /tmp
╰─○ nohup bash -c "python3 script.py" &
[1] 616237
nohup: ignoring input and appending output to 'nohup.out'

Results

image

Let’s charge it !

image

Project took actually less than an hour. (Writing this blogpost took more lol). Now, I don’t need to check the app anymore my server does it for me.

This post is licensed under CC BY 4.0 by the author.