Skip to content

Instantly share code, notes, and snippets.

@pascal-schetelat
Created February 28, 2021 10:01
Show Gist options
  • Save pascal-schetelat/4e31697a4c7e5fe7aeebe35d9053136b to your computer and use it in GitHub Desktop.
Save pascal-schetelat/4e31697a4c7e5fe7aeebe35d9053136b to your computer and use it in GitHub Desktop.
Auth et API du module IP wiser EER31800 de Schneider
my_char = "PzLaM2ZqOx5Ks3NwIcJ1Sd6NeUvH7WfBrY9AbGgVtTnGhVyTmG8EjVu0TkFi4QClRoDpX"
mac_addr = '00:80:F4:xx:xx:xx'
replaceAll = mac_addr.replace(':', '')
def generatePasswd(str1=replaceAll, i=len(replaceAll), str2=my_char):
str3 = str1
i2 = i
str4 = str2
cArr = ['']*12 # 12 elements
if str3 != '':
i3 = 6
if i2 >= 6:
substring = str3[-6:]
i4 = 0
while (i4 < i2) & (i4 < i3):
hexToInt = int(f"0x{substring[i4]}",0)
i5 = 15 - (hexToInt % 15)
d2 = float(hexToInt)
i6 = i4
d3 = float(i5)
round_ = int(
round(d3 ** 3.0) * 8 +
round(d3 ** 2.0) * 3 +
(i5 * 5)) % (len(str2) + 1)
i7 = i6 * 2
idx = int(round(d2 ** 3.0) * 6 +
round(d2 ** 2.0) * 9 +
(hexToInt * 7) )% (len(str2) + 1)
cArr[i7] = str4[idx]
cArr[i7 + 1] = str4[round_]
i4 = i6 + 1
i3 = 6
return ''.join(cArr)
if __name__ == '__main__':
import requests
key = generatePasswd()
r = requests.get('https://192.168.xx.xx/rsa1/MeterInstantData', verify=False, auth=('m2madmin', key))
r.json()
paths = {
"DELETE_INSTANCE_MIO_INPUT" : "/rsa1/MioInput;channel=",
"DELETE_INSTANCE_MIO_OUTPUT" : "/rsa1/MioOutput;channel=",
"DELETE_INSTANCE_MPR" : "/rsa1/MprEndpoint;channel=",
"DELETE_WIRELESS_INPUT" : "/rsa1/MioWirelessInput;slaveId=%d;channel=%d",
"DELETE_WIRELESS_METER" : "/rsa1/WirelessMeter;deviceAddress=",
"DELETE_WIRELESS_OUTPUT" : "/rsa1/MioWirelessOutput;slaveId=%d;channel=%d",
"DELETE_WIRELESS_PRO" : "/rsa1/WirelessProChannel;deviceAddress=",
"DELETE_WIRELESS_SENSOR" : "/rsa1/WirelessSensor;deviceAddress=",
"GET_BRANCH_METERS" : "/rsa1/BranchMeters",
"GET_CONNECTION_STATISTICS" : "/rsa1/WiserConnectionStatistics",
"GET_CONTROLLER" : "/rsa1/Controller",
"GET_EP_CONFIG_UPDATE_STATUS" : "/rsa1/EpConfigUpdateStatus ",
"GET_GATEWAY_KEY" : "/rsa1/GateWayKey",
"GET_INSTANCES_MPR" : "/rsa1/MprEndpoint/instances",
"GET_INSTANCE_MIO_INPUT" : "/rsa1/MioInput/instances",
"GET_INSTANCE_MIO_OUTPUT" : "/rsa1/MioOutput/instances",
"GET_MEASUREMENT_DATA" : "/rsa1/MeasurementData;sId=%d;ch=%d",
"GET_METER_CUMULATED_DATA" : "/rsa1/MeterCumulatedData",
"GET_METER_INSTANT_DATA" : "/rsa1/MeterInstantData",
"GET_PRODUCT_DETAILS" : "/rsa1/ProductDetails",
"GET_PRODUCT_INTERFACE" : "/fct/ProductInterface",
"GET_SYSTEM_UPGRADE_STATUS" : "/rsa1/SystemUpgradeStatus",
"GET_USAGE_METERS" : "/rsa1/UsageMeters",
"GET_USER_PREPAIRING" : "/rsa1/UserPreparing",
"GET_WIRELESS_DIAGNOSTIC" : "/rsa1/WirelessDiagnostic",
"GET_WIRELESS_INPUT" : "/rsa1/MioWirelessInput;slaveId=%d;channel=%d",
"GET_WIRELESS_INPUTS" : "/rsa1/MioWirelessInput/instances",
"GET_WIRELESS_METER" : "/rsa1/WirelessMeter;deviceAddress=",
"GET_WIRELESS_METERS" : "/rsa1/WirelessMeter/instances",
"GET_WIRELESS_METERS_LITE" : "/rsa1/WirelessMeterList/instances",
"GET_WIRELESS_METER_LITE" : "/rsa1/WirelessMeterInfo;dA=",
"GET_WIRELESS_OUTPUT" : "/rsa1/MioWirelessOutput;slaveId=%d;channel=%d",
"GET_WIRELESS_OUTPUTS" : "/rsa1/MioWirelessOutput/instances",
"GET_WIRELESS_PRO_LIST" : "/rsa1/WirelessProChannel/instances",
"GET_WIRELESS_PRO_LITE" : "/rsa1/WirelessProInfo;dA=",
"GET_WIRELESS_SENSOR" : "/rsa1/WirelessSensor;deviceAddress=",
"GET_WIRELESS_SENSORS" : "/rsa1/WirelessSensor/instances",
"GET_WIRELESS_SENSOR_LITE" : "/rsa1/WirelessSensorInfo;dA=",
"GET_WIRELESS_SENSOR_TEMPERATURE" : "/rsa1/TemperatureInfo;sId=",
"POST_BLINK_DEVICE" : "/rsa1/WirelessManager/methods/blinkDevice",
"POST_BUSINESS_INFO" : "/api/v1/fesb/provision",
"POST_EVE_METER" : "/rsa1/LoadStatus/methods/SetStandbyThreshold",
"POST_FIRMWARE_UPLOAD" : "/FirmwareUpload",
"POST_INIT_SYSTEM_UPGRADE" : "/rsa1/Controller/methods/initSystemUpgrade",
"POST_INSTANCE_MIO_CONTROL" : "/rsa1/Controller/methods/operateMio",
"POST_INSTANCE_MIO_INPUT" : "/rsa1/MioInput/instances",
"POST_INSTANCE_MIO_OUTPUT" : "/rsa1/MioOutput/instances",
"POST_INSTANCE_MPR" : "/rsa1/MprEndpoint/instances",
"POST_STATUS" : "/rsa1/LoadStatus/methods/GetStatus",
"PUT_BRANCH_METERS" : "/rsa1/BranchMeters",
"PUT_INSTANCE_MIO_INPUT" : "/rsa1/MioInput;channel=",
"PUT_INSTANCE_MIO_OUTPUT" : "/rsa1/MioOutput;channel=",
"PUT_INSTANCE_MPR" : "/rsa1/MprEndpoint;channel=",
"PUT_USER_PREPAIRING" : "/rsa1/UserPreparing",
"PUT_WIRELESS_CHANNEL" : "/rsa1/WirelessManager",
"PUT_WIRELESS_INPUT" : "/rsa1/MioWirelessInput;slaveId=%d;channel=%d",
"PUT_WIRELESS_METER" : "/rsa1/WirelessMeter;deviceAddress=",
"PUT_WIRELESS_OUTPUT" : "/rsa1/MioWirelessOutput;slaveId=%d;channel=%d",
"PUT_WIRELESS_SENSOR" : "/rsa1/WirelessSensor;deviceAddress=",
"SEM_IDENTIFICATION" : "/rsa1/SemIdentificationOpt",
"START_COMISSIONING" : "/rsa1/WirelessManager/methods/startCommissioning",
"STOP_COMISSIONING" : "/rsa1/WirelessManager/methods/stopCommissioning",
}
@pascal-schetelat
Copy link
Author

pascal-schetelat commented Nov 17, 2021

J'étais aussi passé par la case dé-compilation de l'apk. Comme je n'avais jamais fait de Java j'ai mis un peu de temps pour trouver la partie responsable de l'auth.

Pour les API je ne suis plus très sûr mais je crois que que c'est une fois que j'ai réussi à implémenter l'algo et à interroger le module IP. Il y a une route, genre la route GET_PRODUCT_INTERFACE, qui donne le détail. il faudrait que je retrouve mes notes si besoin.

@Beetix
Copy link

Beetix commented Nov 17, 2021

J'ai justement tenté la dé-compilation étant tombé sur tes réponses à ce post. Malheureusement, dans mon cas, je ne retrouve rien avec grep...

Ok, merci, je vais tenter le GET_PRODUCT_INTERFACE voir ce que ça donne.

@Beetix
Copy link

Beetix commented Nov 18, 2021

Je viens d'envoyer une requête sur la route GET_PRODUCT_INTERFACE.

La réponse n'a pas l'air très exploitable : { "ProtocolConfig": 8, "ProtocolConfigFactory": 1, "Localization": 1 }

@Beetix
Copy link

Beetix commented Nov 19, 2021

J'ai téléchargé l'APK ici et la dé-compilation est beaucoup plus intéressante. J'ai réussi à étudier le code cette fois.

Je viens de réussir à contrôler le contacteur grâce à la route POST_INSTANCE_MIO_CONTROL en postant le JSON suivant :

{
  "slaveId": XXX,
  "channel": XX,
  "status": "ON" ou "OFF"
}

@rroblik
Copy link

rroblik commented Dec 7, 2021

@Beetix hello
Et donc?!

@Inrego
Copy link

Inrego commented Mar 15, 2022

my_char is that a static value? Can it be changed?
I used this like it is, and it was working great. But now I'm getting 401 Unauthorized with the passwords generated here. Any ideas?

@PentaSX
Copy link

PentaSX commented Mar 15, 2022

my_char is that a static value? Can it be changed? I used this like it is, and it was working great. But now I'm getting 401 Unauthorized with the passwords generated here. Any ideas?

Hello,

I've got the same problem since yesterday.
It seems the class has changed (firmware update?)

package com.schneiderelectric.plugin.fwupdate;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class AdminPasswordService {
private static final String M2M_PASSWORD_HASH_ARAMIS = "PzLaM2ZqOx5Ks3NwIcJ1Sd6NeUvH7WfBrY9AbGgVtTnGhVyTmG8EjVu0TkFi4QClRoDpX";
private static final int M2M_PASSWORD_LENGTH = 12;
private static final int M2M_PASSWORD_MAX_SEED_LENGTH = 6;

private static String hashSHA(String string2) {
    MessageDigest messageDigest;
    try {
        messageDigest = MessageDigest.getInstance((String)"SHA-256");
    }
    catch (NoSuchAlgorithmException noSuchAlgorithmException) {
        noSuchAlgorithmException.printStackTrace();
        messageDigest = null;
    }
    messageDigest.update(string2.getBytes(StandardCharsets.UTF_8));
    byte[] arrby = messageDigest.digest();
    Object[] arrobject = new Object[]{new BigInteger(1, arrby)};
    return String.format((String)"%064x", (Object[])arrobject);
}

public static String m2mAdminPassword(String string2) {
    return AdminPasswordService.passwordFromMacAddress(string2);
}

public static String newM2mAdminPassword(String string2, String string3) {
    String string4 = AdminPasswordService.passwordFromMacAddress(string2);
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(string4);
    stringBuilder.append(string3);
    return AdminPasswordService.hashSHA(stringBuilder.toString());
}

private static String passwordFromMacAddress(String string2) {
    int n;
    int n2 = string2.length();
    char[] arrc = new char[12];
    if (string2 != null && n2 >= (n = 6)) {
        String string3 = string2.substring(string2.length() - n);
        int n3 = 0;
        while (n3 < n2 && n3 < n) {
            char c = string3.charAt(n3);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("");
            stringBuilder.append(c);
            int n4 = Integer.parseInt((String)stringBuilder.toString(), (int)16);
            int n5 = 15 - n4 % 15;
            double d = n4;
            long l = 6L * Math.round((double)Math.pow((double)d, (double)3.0));
            int n6 = n3;
            int n7 = (int)(l + 9L * Math.round((double)Math.pow((double)d, (double)2.0)) + (long)(n4 * 7)) % 70;
            double d2 = n5;
            int n8 = (int)(8L * Math.round((double)Math.pow((double)d2, (double)3.0)) + 3L * Math.round((double)Math.pow((double)d2, (double)2.0)) + (long)(n5 * 5)) % 70;
            int n9 = n6 * 2;
            arrc[n9] = M2M_PASSWORD_HASH_ARAMIS.charAt(n7);
            arrc[n9 + 1] = M2M_PASSWORD_HASH_ARAMIS.charAt(n8);
            n3 = n6 + 1;
            n = 6;
        }
    }
    return new String(arrc);
}

}

I can see a new method newM2mAdminPassword

I tired to get GateWayKey from this URL (not protected by auth) but it returns an 503 Service Unavailable error.
https://<wiser_ip>/rsa1/GateWayKey

I extracted it from the APK Wiser Energy 3.9.1

@Inrego
Copy link

Inrego commented Mar 15, 2022

Hmm.. On my phone, I have version 6.7.1 of the app. It can still connect to the device, and it doesn't have that AdminPasswordService class.
So must be possible to connect without that. But maybe it will provide another way in..

Anyway, here are my findings from earlier today:
NicolaiPetri/wiser2mqtt#2 (comment)

You need to press the button before accessing /rsa1/GateWayKey

The key changes every time.

@PentaSX
Copy link

PentaSX commented Mar 15, 2022

Hello,

Thanks you very much for your findings.
I was takling about the Wiser Energy app; not the eSetup which is 6.7.1.
At the moment the access through Wiser Energy app is working (by the Cloud).

I'll give some tries tonight when i'll be at home, but I think the m2madmin password is reset each time you press the button.

The output of the newM2mAdminPassword method is very long now.
So the request could be something like that :
curl -k --user m2madmin:0c1e9718b65aa7c444fb78c1ace930c044ae6ce1b5609816f00a5835e5d4fd79 https://<wiser_ip>/rsa1/MeterInstantData

Check the class here :
https://pastebin.com/t59VjMkf

You can execute it with java and it'll output the password generated from MAC and GateWayKey.

Kind regards.

@verydrunk
Copy link

verydrunk commented Mar 15, 2022

Waiting eagerly, just got all the readings to work with python after NicolaiPetri nice work.
Thx for doing this work

@Inrego
Copy link

Inrego commented Mar 15, 2022

But this AdminPasswordService is for the Wiser Energy app, right? So for cloud connection? We're not sure that it will even work with the local api?
The eSetup app does work with the local api, so I think it best to try to focus around the source code from that app to figure out a way in.

@verydrunk
Copy link

How did u generate the new password, I can't get the java from u'r pastebin to work?

@PentaSX
Copy link

PentaSX commented Mar 15, 2022

1/ Push the button on the EER31800

2/ Get the key via HTTP GET on this URL : https://<wiser_ip>/rsa1/GateWayKey

3/ Convert the key and the MAC address to a password
Use this website : https://www.tutorialspoint.com/compile_java_online.php
Copy paste the class here : https://pastebin.com/raw/t59VjMkf
Add your MAC and GateWayKey.

String MAC = "<PUT_MAC_HERE>";
String GateWayKey = "<PUT_GATEWAYKEY_HERE>";

4/ Send JSON data via HTTP POST to https://<wiser_ip>/rs/login

For exemple :
{
"username": "m2madmin",
"password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

5/ A cookie is returned named SID, get the value of it (SID=yyyyyy)

6/ Make subsequent requests with that cookie by adding it in headers :
Cookie: SID=yyyyyy

It is now working for me 👍

@verydrunk
Copy link

Thanks alot man, u guys are the best!
btw, i figured out what was wrong when i wrote the last post. MAC is of course without ":"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment