💾 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

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

Private API and lazy programmers

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.

image

image

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?

image

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 :).