diff --git a/content/posts/2024-03-26-playing-with-snu-eam.md b/content/posts/2024-03-26-playing-with-snu-eam.md index 9ec980b..9379c3c 100644 --- a/content/posts/2024-03-26-playing-with-snu-eam.md +++ b/content/posts/2024-03-26-playing-with-snu-eam.md @@ -7,21 +7,24 @@ description: "Playing with SNU EAM" ## Playing with "Electronic Attendace System" of SNU. -**You know, lots of school uses beacon-based digital attendance system, and so do our school. It seems... hackable, isn't it? So let's dig deep what is going on there and ultimately taking an attendace without actaully attending. Of course its not for real use, it's just for fun!** +**A lots of school uses beacon-based digital attendance system, and so do our school. It seems relatively hackable, isn't it? So let's dig deep what's going on there and ultimately taking an attendace without actaully attending. Of course its not for real use, it's just for fun!** + +[Repository](https://github.com/sv64/snuapp) ### 1. APK JADX analyzing -I just decompiled apk using JADX and renamed every function and class (which I understand) for two straight days. But it didnt matter that much. Important things were obsfucated in JNI. What a shame. +I decompiled apk using JADX and renamed every function and classes.(which I understand) But it didnt matter that much because mportant things were obsfucated in JNI. We need to disassemble and analyze JNI functions. +Also, by looking at internal logic, I discovered that there isnt any authentication going on, it just read attributes of beacon which is very likely to be fixed, and send it to server. So actually we didnt need to think about iBeacon reading thing in Android. We need to look at what it sends to server. ### 2. App MITM patch, repackaging. -Since we knew there is nothing going on in Beacon itself, and it just talks to server to do attendance things, now we have to analyze its packets, and ultimately replay it. - +So now we have to analyze its packets to potentially replay it by ourself. Using `apk-mitm` and `uber-apk-signer`, I modified app for use with mitmproxy. Since app has its own signature verifing on startup and some activity, we have to bypass that verification. 1. Startup -It retrieves App signature from `https://mob.snu.ac.kr/api/versionCheck.action`. Using mitmproxy script, we can easily modify its response. + +It retrieves App signature from `https://mob.snu.ac.kr/api/versionCheck.action` on splash screen. Using mitmproxy script, we can easily modify server's response. ```python def response(flow: http.HTTPFlow) -> None: @@ -34,8 +37,7 @@ def response(flow: http.HTTPFlow) -> None: 2. JNI Signature checking. -It uses JNI `libEncryptionKeyStore.so` for EncryptionKeyRetrieving and checkRoot. -In checkRoot, it verifies app's signature by just with some loops. When it doesnt match, it branches into std::terminate. +It uses JNI `libEncryptionKeyStore.so` for additional checkRoot. Name is checkRoot, but what it does is just verifies app's signature with some simple loops and direct comparison. When it doesnt match, it branches into std::terminate. ```asm 00007c4c if (*(data_3d158 - 0x18) == 0) @@ -47,7 +49,7 @@ In checkRoot, it verifies app's signature by just with some loops. When it doesn 00007d68 *x0_8 = 0x35 ``` -We can either modify binary as our signature, but replacing every single bytes would be nightmare. So I had to do it automatically. To track down where every bytes are assigned, I just sweeped and parsed every single line and modified it. I matched binary expressions into opcode and tracked down when and where it stored bytes into string register, and changed it into my signature. ([`armv8.py`](scripts/armv8.py) and [`ubivelox.py`](scripts/ubivelox.py)) +Since original signature is hard-coded into binary, we can either modify binary as our signature. But replacing every single bytes manually would be a nightmare. So I had to do it automatically. To track down where every bytes are assigned, I just sweeped and parsed every single line. I matched binary expressions into opcode and tracked down when and where it stored bytes into string register, and changed it into my signature. ([`armv8.py`](scripts/armv8.py) and [`ubivelox.py`](scripts/ubivelox.py)) This is of course very very inefficient and very limited way, but it worked well for me this time. Its one time script, no one cares about it. Or we can just bypass `b std::terminate` to `ret`. It doesnt return anything, so just returning should be fine. @@ -119,7 +121,7 @@ They wrote code to do some kind of key rotating with that index above, and that 0000721c 2c068052 mov w12, #0x31 00007220 ab070139 strb w11, [x29, #0x41 {var_20+0x1}] 00007224 ab068052 mov w11, #0x35 -```` +``` Each subroutine builds key into var_30, var_20, ... , so I just need to write down these keys right? @@ -227,7 +229,7 @@ JNIEXPORT jstring JNICALL Java_com_ubivelox_security_EncryptionKeyStore_getSecre } ``` -So I just did that. What a easy tasks it was. Now I can just get EncryptionKey with logcat. +So I just did that. What a easy tasks it was. Now I can just get encryption key using `adb logcat`. > ``` > 03-26 11:04:03 E libEncryptionKeyStore: RETURN: 3172XXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -235,16 +237,13 @@ So I just did that. What a easy tasks it was. Now I can just get EncryptionKey w > 03-26 11:04:03 E libEncryptionKeyStore: RETURN: 3172XXXXXXXXXXXXXXXXXXXXXXXXXXXX > 03-26 11:04:03 E libEncryptionKeyStore: RETURN: 3172XXXXXXXXXXXXXXXXXXXXXXXXXXXX > ``` - > It was really this easy.. I should've done it from beginning. - -And at this moment I realized this output was just endian converted from my extracted key above. - +> It was really this easy.. I should've done it from beginning. ### 4 -So, I now extracted EncryptionKey, and I have all packets app talked to server. Now its time to decrypt all messages and build our own requests! +So, I now extracted Encryption Key, and I have all packets containing all informations about attendace. Now its time to decrypt messages and build our own requests! -Messages are encrypted in AES-CBC mode with IV of just 0s, padded with pkcs#5. +Messages were encrypted in AES-CBC mode with IV of just 0s, padded with PKCS#5. I modified mitm script to automatically decrypt messages and log them. @@ -266,7 +265,7 @@ elif "https://scard1.snu.ac.kr/" in url: f.write("\n------------------\n") ``` -And my log looked like this. Yay! +And my log looked like this. ``` 09:52:22 @@ -279,17 +278,20 @@ https://scard1.snu.ac.kr/eaas/R001 {"trVer":"1.0.0","trId":"R001","sysTime":"20240326095222","ssoToken":"","resCd":"000000","resMsg":"","enc":"32942ddxxxxxxxxxxxxxxx7a24b2cf3b"} ``` -Other things were clear, and that `enc` was just SHA1 of body content, for verifing maybe. -I have all of informations, now I can just send requests to server and do attendance, retrieve class and lecture infos. +Everything was clear, and that `enc` was just SHA1 of body content for verifing maybe. +I have all of informations, now I can just send requests to server, retrieve class and lecture infos and do attendance. ### 5 + As in case of Attendace Checking, It uses BLE Beacon (or iBeacon). At first I thought there should be some kind of randomization and special ble service, but it just sends existing and constant beacon data (UUID, MacAddress, ...) to server. -It seems easy to manipulate, and by knowing this I can just build BLE server with matching UUID and such properties, and use unmodified app to do attendance as usual. Modifing and sending custom built raw packets could be illegal in some circumstances, so this way is more safe and undetectable. +It seemed easy to manipulate, and by knowing this, I can just build my BLE server with matching UUID and such properties, and use unmodified app to do attendance as usual. Modifing and sending custom built raw packets could be illegal in some circumstances, so this way is more safe and undetectable. -Attendance packets involves encryption twice. First as other encrypts header, and second it encrypts BLE beacon data one more time in header. I dont know why they encrypt twice, but there should be some context in it. +Attendance packets involves encryption twice. Once in other encrypts header, and twice it encrypts BLE beacon data in header. I dont know why they encrypt twice, but there should be some context in it. + +Using all of informations, ```python bleInfo = { @@ -321,12 +323,12 @@ Please dont use it in real world. It is easily detectable on server side. I risk ### Afterthoughts. -It seem complex at the beginning and solution was pretty easy and straightforward. I bet anyone could do it if one puts some times in it, I wonder why this wasnt the case. Maybe because it is actually illegal to do so, and because of that it was less appealing? I dont know. +It seemed very complex at the beginning but solution was pretty easy and straightforward. I bet anyone could do it if one puts some times in it, I wonder why this wasnt the case. Maybe because it's actually illegal to do, and because of that it was less appealing to do it? I dont know.. Also, hardcoding encryption keys or verification tokens inside the app itself seems dangerous, as I could extract keys from decompilation. And any app has potential to be preloaded and sniffed, especially JNI like this case, so using JNI to retrive raw keys is not good I think. Its more better to just encrypt inside JNI so that key itself can be contained in itself, right? I dont think there is a way to contain key in blackbox even for end user. User has all access to app so it is hard to hide key from them maybe. I dont have deep knowledge in this area.. Even TPM is vulnerable to hardware key sniffing, so using hardware blackboxes are also not that sufficient unless it is really really blackbox. Maybe ZKP can solve this problem? -Signature verifing is hard. Using Network is dangerous, and using internal logic can be also bypassed. I think only way to prevent this is in operating system, like in iOS it's much more hard to do this kind of job. +`mitmproxy` done most important job in this case. Nowadays apps uses certificate pinning, but it was also useless with repackaging... So any problems is all sign verification and I think there is no way to perfectly do it. -mitmproxy must be the most threat in this case? Nowadays apps uses cert pinning, but it was also useless... So any problems is all sign verification... Like Play Protect perhaps? +Signature verifing is hard. Using Network is dangerous, and using internal logic can be also bypassed. I think only way to prevent this is in operating system, like in iOS it's much more hard to do this kind of job. \ No newline at end of file