💾 Archived View for 0x80.org › gemlog › 2014-08-11-private-api.gmi captured on 2022-04-28 at 17:41:06. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
Whenever I get curious about some website or service that provides some kind of an application for a phone Android or Iphone. I get a little bit curious on how did they design their API, how does their application communicate with their services, and so on. What I found out in many curious tests is that programmers suck, they are usually lazy. They tend to get lazy fast, and do the easiest ways which are not necessarly the right way. So Yestarday I used some website which also provides an Android and an Iphone application. I got curious. Downloaded the Android version, unpacked it and started reverse engineering and undertsanding classes.dex file and how the implemented the communication to their private API. I'm sure that the programmers are either really bad or just got lazy since they thought who will want to reverse engineer our phone application and find flaws in our API? Well... me ^_^.
So we start by getting a general idea of how their code works. I usually start by seeing who calls the function that does a general communication something that sends stuff to somewhere. It's always a good place since this is what their application do. Send stuff, and get stuff from somewhere.
I find that they used AsyncHttpClient a lot so we look at who calls AsyncHttpClient.get and AsyncHttpClient.post to get a general idea of how things look.
So those two functions are called from one place AppRestClient as AppRestClient.get for GET requests, and .post for POST requets. Anyway now we look at who calls for example AppRestClient.post to see who sends stuff and what stuff are sent and how?
This is the path to all functions doing POST requet. Let us take a look at how for example the UploadManager does uploading of images by communicating with the API.
CODE:000C616C # Method 13341 (0x341d) CODE:000C616C word_C616C:.short 0xB # DATA XREF: CODE:000653FDi CODE:000C616C # Number of registers : 0xb CODE:000C616E .short 4 # Size of input args (in words) : 0x4 CODE:000C6170 .short 5 # Size of output args (in words) : 0x5 CODE:000C6172 .short 3 # Number of try_items : 0x3 CODE:000C6174 .int word_1E33EE # Debug info CODE:000C6178 .int 0x4D # Size of bytecode (in 16-bit units): 0x4d CODE:000C617C # Source file: xxxxx.java CODE:000C617C private void com.xxxx.utils.UploadManager.startUploadImageToServer( CODE:000C617C int productId, CODE:000C617C java.lang.String imageUri, CODE:000C617C int imageIndex) CODE:000C617C this = v7 # CODE XREF: UploadManager_imageUpload@VI+58j CODE:000C617C productId = v8 CODE:000C617C imageUri = v9 CODE:000C617C imageIndex = v10 CODE:000C617C jobject = v2 CODE:000C617C imgurifmt = v4 CODE:000C617C jsonhdrstr = v5 CODE:000C617C uphndlr = v6 CODE:000C617C .prologue_end CODE:000C617C .line 219 CODE:000C617C CODE:000C617C new-instance jobject, <t: JSONObject> CODE:000C6180 invoke-direct {jobject}, <void JSONObject.<init>() imp. @ _def_JSONObject__init_@V> CODE:000C6186 # try 0xC6186-0xC61B6: CODE:000C6186 CODE:000C6186 loc_C6186: # DATA XREF: CODE:000C6218i CODE:000C6186 .local name:'jdata' type:'Lorg/json/JSONObject;' # "productId" CODE:000C6186 jdata = v2 CODE:000C6186 .line 222 CODE:000C6186 CODE:000C6186 const-string v3, aProductid CODE:000C618A invoke-virtual {jdata, v3, productId}, <ref JSONObject.put(ref, int) imp. @ _def_JSONObject_put@LLI> CODE:000C6190 .line 226 CODE:000C6190 CODE:000C6190 invoke-virtual {this, imageUri}, <boolean UploadManager.itsOnlineImage(ref) UploadManager_itsOnlineImage@ZL> CODE:000C6196 move-result v3 CODE:000C6198 if-eqz v3, convert_img_to_base64 CODE:000C619C .line 229 CODE:000C619C CODE:000C619C const-string v3, aProductimage # "productImage" CODE:000C61A0 invoke-virtual {jdata, v3, imageUri}, <ref JSONObject.put(ref, ref) imp. @ _def_JSONObject_put@LLL> CODE:000C61A6 CODE:000C61A6 loc_C61A6: # CODE XREF: UploadManager_startUploadImageToServer@VILI+7Cj CODE:000C61A6 .line 236 CODE:000C61A6 CODE:000C61A6 if-nez imageIndex, prod_is_main_img CODE:000C61AA .line 237 CODE:000C61AA CODE:000C61AA const-string v3, aProductismaini # "productIsMainImage" CODE:000C61AE const/4 imgurifmt, 0 CODE:000C61B0 invoke-virtual {jdata, v3, imgurifmt}, <ref JSONObject.put(ref, int) imp. @ _def_JSONObject_put@LLI> CODE:000C61B6 # try 0xC61B6-0xC61E4: CODE:000C61B6 CODE:000C61B6 loc_C61B6: # CODE XREF: UploadManager_startUploadImageToServer@VILI+80j CODE:000C61B6 # UploadManager_startUploadImageToServer@VILI+8Ej CODE:000C61B6 # DATA XREF: ... CODE:000C61B6 .line 250 CODE:000C61B6 CODE:000C61B6 new-instance v1, <t: StringEntity> CODE:000C61BA invoke-virtual {jdata}, <ref JSONObject.toString() imp. @ _def_JSONObject_toString@L> CODE:000C61C0 move-result-object v3 CODE:000C61C2 invoke-direct {v1, v3}, <void StringEntity.<init>(ref) imp. @ _def_StringEntity__init_@VL> CODE:000C61C8 .local name:'entity' type:'Lorg/apache/http/entity/StringEntity;' CODE:000C61C8 entity = v1 CODE:000C61C8 .line 252 CODE:000C61C8 CODE:000C61C8 iget-object v3, this, UploadManager_context CODE:000C61CC const-string imgurifmt, a?actionAddimag # "?action=addimgtoprod" CODE:000C61D0 const-string jsonhdrstr, aApplicationJso # "application/json" CODE:000C61D4 .line 253 CODE:000C61D4 CODE:000C61D4 new-instance uphndlr, <t: UploadManager$JsonHttpResponseHandlerUploadingImage> CODE:000C61D8 invoke-direct {uphndlr, this, productId, imageIndex}, <void UploadManager$JsonHttpResponseHandlerUploadingImage.<init>(ref, int, int) UploadManager$JsonHttpResponseHandlerUploadingImage__init_@VLII> CODE:000C61DE .line 252 CODE:000C61DE CODE:000C61DE invoke-static {v3, imgurifmt, entity, jsonhdrstr, uphndlr}, <void AppRestClient.post(ref, ref, ref, ref, ref) AppRestClient_post@VLLLLL> CODE:000C61E4 .end local 'entity' CODE:000C61E4 CODE:000C61E4 locret: # CODE XREF: UploadManager_startUploadImageToServer@VILI+98j CODE:000C61E4 .line 260 CODE:000C61E4 CODE:000C61E4 return-void CODE:000C61E6 # --------------------------------------------------------------------------- CODE:000C61E6 # try 0xC61E6-0xC620A: CODE:000C61E6 CODE:000C61E6 convert_img_to_base64: CODE:000C61E6 # CODE XREF: UploadManager_startUploadImageToServer@VILI+1Cj CODE:000C61E6 # DATA XREF: CODE:000C6228i CODE:000C61E6 .line 234 # "productImage" CODE:000C61E6 CODE:000C61E6 const-string v3, aProductimage CODE:000C61EA invoke-direct {this, imageUri}, <ref UploadManager.getBase64StringFromImage(ref) UploadManager_getBase64StringFromImage@LL> CODE:000C61F0 move-result-object imgurifmt CODE:000C61F2 invoke-virtual {jdata, v3, imgurifmt}, <ref JSONObject.put(ref, ref) imp. @ _def_JSONObject_put@LLL> CODE:000C61F8 goto loc_C61A6 CODE:000C61FA # --------------------------------------------------------------------------- CODE:000C61FA # catch Exception: CODE:000C61FA CODE:000C61FA imgexception: # DATA XREF: CODE:000C6234i CODE:000C61FA .line 242 CODE:000C61FA CODE:000C61FA move-exception v3 CODE:000C61FC goto loc_C61B6 CODE:000C61FE # --------------------------------------------------------------------------- CODE:000C61FE CODE:000C61FE prod_is_main_img: CODE:000C61FE # CODE XREF: UploadManager_startUploadImageToServer@VILI:loc_C61A6j CODE:000C61FE .line 239 # "productIsMainImage" CODE:000C61FE CODE:000C61FE const-string v3, aProductismaini CODE:000C6202 const/4 imgurifmt, 1 CODE:000C6204 invoke-virtual {jdata, v3, imgurifmt}, <ref JSONObject.put(ref, int) imp. @ _def_JSONObject_put@LLI> CODE:000C620A goto loc_C61B6 CODE:000C620C # --------------------------------------------------------------------------- CODE:000C620C # catch UnsupportedEncodingException: CODE:000C620C CODE:000C620C loc_C620C: # DATA XREF: CODE:000C6238i CODE:000C620C .line 256 CODE:000C620C CODE:000C620C move-exception v0 CODE:000C620E .line 258 CODE:000C620E CODE:000C620E invoke-virtual {v0}, <void UnsupportedEncodingException.printStackTrace() imp. @ _def_UnsupportedEncodingException_printStackTrace@V> CODE:000C6214 goto locret CODE:000C6214 Method End
The above simply says that the function creates a JSON object this object contains a product ID, and an imageuri. The product Id is of type int the imageuri is of two types. If the user provided an image then the image is converted to base64 and added to that json object, otherwise then it's a link that is added to the json object. Also the json object contain another member called product is a main image which is a int that acts as a boolean which specifices some property of the image, that it's a main image or whatever. Anyway this processed to the communicating with the API part. It converts the json object to a string and sends it to the api as POST data with content-type: application/json and does not check if it was sucessful or a failure. So here's the problem that I see many times. The programmer enforces rules and restrictions inside the application but the API is free to do anything. That's because the programmer is lazy or gives no shit about security. Just on this part of the API we could just encode any base64 string sends it and it will upload the data to the server no checking no ownership. So we could for example iterate over all product ids sending our own image as main page and in couple seconds we'll change all images on the server. :< that's bad. Well, what's worse is that every feature the api is capabile of that I have checked are vulnerable since it doesn't matter who owns what or who can or can't do what. The design is bad and many people who write such code doesn't care for any kind of security. I have seen this many times on many sites and apps.
This specific application uses 12 api commands all of which doesn't enforce or check the authentication of the user, but any kind of checking or whatever is happend on the application level and not on the api level.
What I have learned is that programmers don't secure the things that they assume no one will use or find out :).