forked from shihaam/thijooree
working mib login and list accounts
This commit is contained in:
@@ -0,0 +1,449 @@
|
||||
# MIB Faisanet — Login Flow
|
||||
|
||||
Fully reverse engineered from a captured HAR trace of a first-time device
|
||||
registration followed immediately by a regular login.
|
||||
|
||||
---
|
||||
|
||||
## Key Corrections
|
||||
|
||||
The DH parameter names in the app's source are **misleading**:
|
||||
|
||||
| App variable | DH role | Value |
|
||||
|---|---|---|
|
||||
| `A_VALUE` | Exponent / client private key | `15635168026...` (shorter) |
|
||||
| `P_VALUE` | Prime modulus | `24103124269...` (longer) |
|
||||
| `G_VALUE` | Generator | `2` |
|
||||
|
||||
The session key derivation is:
|
||||
```python
|
||||
shared = pow(smod, A_VALUE, P_VALUE) # NOT pow(smod, P_VALUE, A_VALUE)
|
||||
sha256_hex = SHA256(str(shared)).upper()
|
||||
session_key = base64(bytes.fromhex(sha256_hex))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The full login sequence consists of two phases:
|
||||
|
||||
| Phase | Purpose | Key used |
|
||||
|---|---|---|
|
||||
| **Phase 1** — Device registration | First time this device+account pair is seen | DH session key from `sfunc=r` |
|
||||
| **Phase 2** — Regular login | Every subsequent login | key1/key2 (from phase 1) → second DH → new session key |
|
||||
|
||||
---
|
||||
|
||||
## Full Flow Diagram
|
||||
|
||||
```
|
||||
Client Server
|
||||
| |
|
||||
| [0] sfunc=r (DEFAULT_KEY) |
|
||||
| { cmod, appId, routePath:S40, ... } |
|
||||
|--------------------------------------------->|
|
||||
| { smod, nonceGenerator, xxid, ... } |
|
||||
|<---------------------------------------------|
|
||||
| derive session_key_1 = DH(smod) |
|
||||
| |
|
||||
| [1] sfunc=n routePath:A44 (session_key_1)|
|
||||
| { uname } |
|
||||
|--------------------------------------------->|
|
||||
| { loginType, userSalt } |
|
||||
|<---------------------------------------------|
|
||||
| |
|
||||
| [2] sfunc=n routePath:C41 (session_key_1)| ← device registration init
|
||||
| { uname, pgf03, clientSalt } |
|
||||
|--------------------------------------------->|
|
||||
| { key1, key2, otpTypes, fullName, ... } |
|
||||
|<---------------------------------------------|
|
||||
| |
|
||||
| [3] sfunc=n routePath:C42 (session_key_1)| ← OTP verify (registration)
|
||||
| { otp, uname, otpType } |
|
||||
|--------------------------------------------->|
|
||||
| { key1, key2, encryptionMethod:2, ... } |
|
||||
|<---------------------------------------------|
|
||||
| store key1, key2 on device |
|
||||
| |
|
||||
| [4] sfunc=i (key1) | ← second DH key exchange
|
||||
| { cmod, appId, routePath:S40, key2 } |
|
||||
|--------------------------------------------->|
|
||||
| { smod, nonceGenerator, xxid, ... } |
|
||||
|<---------------------------------------------|
|
||||
| derive session_key_2 = DH(smod) |
|
||||
| |
|
||||
| [5] sfunc=n routePath:A44 (session_key_2)|
|
||||
| { uname } |
|
||||
|--------------------------------------------->|
|
||||
| { loginType, userSalt } |
|
||||
|<---------------------------------------------|
|
||||
| |
|
||||
| [6] sfunc=n routePath:A41 (session_key_2)| ← regular login init
|
||||
| { uname, pgf03, clientSalt, requireBankData:1 }|
|
||||
|--------------------------------------------->|
|
||||
| { primaryOTPType, otpTypes, email, uuid, uuid2, ... }|
|
||||
|<---------------------------------------------|
|
||||
| |
|
||||
| [7] sfunc=n routePath:P41 (session_key_2)| ← fetch profile image
|
||||
| { imageHash } |
|
||||
|--------------------------------------------->|
|
||||
| { profileImage (base64 JPEG) } |
|
||||
|<---------------------------------------------|
|
||||
| |
|
||||
| [8] sfunc=n routePath:A42 (session_key_2)| ← OTP verify (regular login)
|
||||
| { otp, uname, otpType } |
|
||||
|--------------------------------------------->|
|
||||
| { ... session established ... } |
|
||||
|<---------------------------------------------|
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Reference
|
||||
|
||||
### [0] Initial Key Exchange — `sfunc=r`
|
||||
|
||||
**Key**: `DEFAULT_KEY = 8M3L9SBF1AC4FRE56788M3L9SBF1AC4FRE5678`
|
||||
|
||||
**Request body** (inner `data` field):
|
||||
```json
|
||||
{
|
||||
"cmod": "<G^A_VALUE mod P_VALUE as decimal string>",
|
||||
"appId": "IOS17.2-<15 random chars>",
|
||||
"routePath": "S40",
|
||||
"sodium": "<random 20-bit int>",
|
||||
"xxid": "<random 40-bit int>"
|
||||
}
|
||||
```
|
||||
|
||||
**Full request** (outer wrapper, encrypted together):
|
||||
```json
|
||||
{ "sfunc": "r", "data": { ...above... } }
|
||||
```
|
||||
|
||||
**Response** (decrypted with DEFAULT_KEY):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "201",
|
||||
"reasonText": "Key generated successfully.",
|
||||
"smod": "<server DH public key>",
|
||||
"nonceGenerator": "<instruction string>",
|
||||
"xxid": "<session token — carry for all subsequent calls>",
|
||||
"sodium": "<server random>",
|
||||
"encMethod": 2
|
||||
}
|
||||
```
|
||||
|
||||
After this step:
|
||||
- Derive `session_key_1 = derive_session_key(smod)`
|
||||
- Save `xxid` and `nonceGenerator`
|
||||
|
||||
---
|
||||
|
||||
### [1] Get Auth Type — `sfunc=n`, `routePath: A44`
|
||||
|
||||
**Key**: `session_key_1`
|
||||
|
||||
**Request** (encrypted):
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"uname": "<username>",
|
||||
"nonce": "<computed from nonceGenerator>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random 20-bit int>",
|
||||
"routePath": "A44",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "108",
|
||||
"reasonText": "Auth type retrieved!",
|
||||
"data": [
|
||||
{
|
||||
"loginType": "1",
|
||||
"userSalt": "<server salt for password hashing>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### [2] Device Registration Init — `sfunc=n`, `routePath: C41`
|
||||
|
||||
First-time only. Registers this device+account pair.
|
||||
|
||||
**Key**: `session_key_1`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"uname": "<username>",
|
||||
"pgf03": "<salted password hash — see below>",
|
||||
"clientSalt": "<random 32-char string>",
|
||||
"nonce": "<nonce>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random>",
|
||||
"routePath": "C41",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "201",
|
||||
"reasonText": "Registration Initialization Successfully.",
|
||||
"primaryOTPType": "3",
|
||||
"roleName": "Consumer Premium",
|
||||
"otpTypes": [2, 3],
|
||||
"fullName": "<user's full name>",
|
||||
"lastLoginTime": "<datetime>",
|
||||
"customerImgHash": "<hash>"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### [3] OTP Verification (Registration) — `sfunc=n`, `routePath: C42`
|
||||
|
||||
**Key**: `session_key_1`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"otp": "<6-digit OTP>",
|
||||
"uname": "<username>",
|
||||
"otpType": "3",
|
||||
"nonce": "<nonce>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random>",
|
||||
"routePath": "C42",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "101",
|
||||
"reasonText": "registration success",
|
||||
"data": [
|
||||
{
|
||||
"appId": "<appId>",
|
||||
"createdDate": "<datetime>",
|
||||
"key1": "<device credential 1 — store securely>",
|
||||
"key2": "<device credential 2 — store securely>",
|
||||
"encryptionMethod": "2",
|
||||
"appAgent": "android/1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`key1` and `key2` are long-lived device credentials. `key1` is the Blowfish key
|
||||
for the next `sfunc=i` call. `key2` is sent as plaintext in the outer wrapper of
|
||||
that call.
|
||||
|
||||
---
|
||||
|
||||
### [4] Authenticated Key Exchange — `sfunc=i`
|
||||
|
||||
Second DH exchange, authenticated with the device credentials.
|
||||
|
||||
**Key**: `key1`
|
||||
|
||||
**Request** (outer wrapper includes `key2`):
|
||||
```json
|
||||
{
|
||||
"sfunc": "i",
|
||||
"key2": "<key2 from registration>",
|
||||
"data": {
|
||||
"cmod": "<G^A_VALUE mod P_VALUE>",
|
||||
"appId": "<appId>",
|
||||
"routePath": "S40",
|
||||
"sodium": "<random 20-bit int>",
|
||||
"xxid": "<random 40-bit int>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (decrypted with `key1`):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "201",
|
||||
"reasonText": "Key generated successfully.",
|
||||
"smod": "<new server DH public key>",
|
||||
"nonceGenerator": "<new instruction string>",
|
||||
"xxid": "<new session token>",
|
||||
"encMethod": 2
|
||||
}
|
||||
```
|
||||
|
||||
After this step:
|
||||
- Derive `session_key_2 = derive_session_key(smod)`
|
||||
- Replace `xxid` and `nonceGenerator` with new values
|
||||
|
||||
---
|
||||
|
||||
### [5] Get Auth Type — `sfunc=n`, `routePath: A44`
|
||||
|
||||
Same as step [1] but with `session_key_2`. Fetches `userSalt` for password hashing.
|
||||
|
||||
---
|
||||
|
||||
### [6] Regular Login Init — `sfunc=n`, `routePath: A41`
|
||||
|
||||
**Key**: `session_key_2`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"uname": "<username>",
|
||||
"pgf03": "<salted password hash>",
|
||||
"clientSalt": "<random 32-char string>",
|
||||
"pmodTime": 0,
|
||||
"requireBankData": 1,
|
||||
"nonce": "<nonce>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random>",
|
||||
"routePath": "A41",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "104",
|
||||
"reasonText": "Initialization Successful",
|
||||
"primaryOTPType": "3",
|
||||
"roleName": "Consumer Premium",
|
||||
"otpTypes": [2, 3],
|
||||
"email": "<masked email>",
|
||||
"uuid": "<uuid1>",
|
||||
"uuid2": "<uuid2>",
|
||||
"xxid": "<xxid>"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### [7] Get Profile Image — `sfunc=n`, `routePath: P41`
|
||||
|
||||
**Key**: `session_key_2`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"imageHash": "<customerImgHash from step 2/6>",
|
||||
"nonce": "<nonce>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random>",
|
||||
"routePath": "P41",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"reasonCode": "201",
|
||||
"reasonText": "Image Found",
|
||||
"profileImage": "<base64 JPEG>"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### [8] OTP Verification (Login) — `sfunc=n`, `routePath: A42`
|
||||
|
||||
**Key**: `session_key_2`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"sfunc": "n",
|
||||
"xxid": "<session xxid>",
|
||||
"data": {
|
||||
"otp": "<6-digit OTP>",
|
||||
"uname": "<username>",
|
||||
"otpType": "3",
|
||||
"nonce": "<nonce>",
|
||||
"appId": "<appId>",
|
||||
"sodium": "<random>",
|
||||
"routePath": "A42",
|
||||
"xxid": "<session xxid>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Password Hashing (`pgf03`)
|
||||
|
||||
The password is never sent in plaintext. The scheme prevents replay attacks by
|
||||
mixing in a server-provided salt and a client-generated random salt.
|
||||
|
||||
```
|
||||
pgf03 = SHA256( clientSalt + SHA256( userSalt + SHA256( password ) ) )
|
||||
```
|
||||
|
||||
All SHA256 values are uppercase hex strings.
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
|
||||
def compute_pgf03(password: str, user_salt: str, client_salt: str) -> str:
|
||||
h1 = hashlib.sha256(password.encode()).hexdigest().upper()
|
||||
h2 = hashlib.sha256((user_salt + h1).encode()).hexdigest().upper()
|
||||
h3 = hashlib.sha256((client_salt + h2).encode()).hexdigest().upper()
|
||||
return h3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Route Paths Summary
|
||||
|
||||
| routePath | sfunc | Description |
|
||||
|---|---|---|
|
||||
| `S40` | `r` or `i` | DH key exchange |
|
||||
| `A44` | `n` | Get auth type / userSalt |
|
||||
| `A41` | `n` | Regular login initialization |
|
||||
| `A42` | `n` | OTP verification (regular login) |
|
||||
| `C41` | `n` | Device registration initialization |
|
||||
| `C42` | `n` | OTP verification (registration) |
|
||||
| `P41` | `n` | Get profile image |
|
||||
| `P40` | `n` | Update profile image |
|
||||
| `P42` | `n` | Delete profile image |
|
||||
Reference in New Issue
Block a user