Add support for importing decrypted Steam JSON blob

Some people have managed to snatch the OTP details from Steam using
Xposed while it is being decrypted by the app. Aegis still won't be
able to do the decryption part, but we can add support for importing
the decrypted JSON blob, which only differs slightly from the old
format.
This commit is contained in:
Alexander Bakker 2023-11-30 21:01:27 +01:00
parent adaae9e6d6
commit ff233090f8
4 changed files with 39 additions and 10 deletions

View file

@ -18,6 +18,10 @@ import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SteamImporter extends DatabaseImporter {
private static final String _subDir = "files";
@ -57,29 +61,43 @@ public class SteamImporter extends DatabaseImporter {
try {
byte[] bytes = IOUtils.readAll(stream);
JSONObject obj = new JSONObject(new String(bytes, StandardCharsets.UTF_8));
return new State(obj);
List<JSONObject> objs = new ArrayList<>();
if (obj.has("accounts")) {
JSONObject accounts = obj.getJSONObject("accounts");
Iterator<String> keys = accounts.keys();
while (keys.hasNext()) {
String key = keys.next();
objs.add(accounts.getJSONObject(key));
}
} else {
objs.add(obj);
}
return new State(objs);
} catch (IOException | JSONException e) {
throw new DatabaseImporterException(e);
}
}
public static class State extends DatabaseImporter.State {
private JSONObject _obj;
private final List<JSONObject> _objs;
private State(JSONObject obj) {
private State(List<JSONObject> objs) {
super(false);
_obj = obj;
_objs = objs;
}
@Override
public Result convert() {
Result result = new Result();
try {
VaultEntry entry = convertEntry(_obj);
result.addEntry(entry);
} catch (DatabaseImporterEntryException e) {
result.addError(e);
for (JSONObject obj : _objs) {
try {
VaultEntry entry = convertEntry(obj);
result.addEntry(entry);
} catch (DatabaseImporterEntryException e) {
result.addError(e);
}
}
return result;

View file

@ -263,6 +263,16 @@ public class DatabaseImporterTest {
}
}
@Test
public void testImportSteamOld() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(SteamImporter.class, "steam_old.json");
for (VaultEntry entry : entries) {
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setIssuer("Steam");
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportAuthenticatorPlus() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importEncrypted(AuthenticatorPlusImporter.class, "authenticator_plus.zip", encryptedState -> {

View file

@ -1 +1 @@
{"steamid":"1234","shared_secret":"THIl8+Jl6ugxr8x0X6eRMg==","serial_number":"12345678901234567890","revocation_code":"R1234","uri":"otpauth:\/\/totp\/Steam:Sophia?secret=JRZCL47CMXVOQMNPZR2F7J4RGI&issuer=Steam","server_time":"0","account_name":"Sophia","token_gid":"894820a474c9","identity_secret":"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n","secret_1":"eWV0IGFub3RoZXIgdGVzdCBzdHJpbmc=","status":1,"steamguard_scheme":"2"}
{"accounts":{"abcdefg":{"steamid":"1234","shared_secret":"THIl8+Jl6ugxr8x0X6eRMg==","serial_number":"12345678901234567890","revocation_code":"R1234","uri":"otpauth:\/\/totp\/Steam:Sophia?secret=JRZCL47CMXVOQMNPZR2F7J4RGI&issuer=Steam","server_time":"0","account_name":"Sophia","token_gid":"894820a474c9","identity_secret":"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n","secret_1":"eWV0IGFub3RoZXIgdGVzdCBzdHJpbmc=","status":1,"steamguard_scheme":"2"}}}

View file

@ -0,0 +1 @@
{"steamid":"1234","shared_secret":"THIl8+Jl6ugxr8x0X6eRMg==","serial_number":"12345678901234567890","revocation_code":"R1234","uri":"otpauth:\/\/totp\/Steam:Sophia?secret=JRZCL47CMXVOQMNPZR2F7J4RGI&issuer=Steam","server_time":"0","account_name":"Sophia","token_gid":"894820a474c9","identity_secret":"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n","secret_1":"eWV0IGFub3RoZXIgdGVzdCBzdHJpbmc=","status":1,"steamguard_scheme":"2"}