记录黑客技术中优秀的内容, 传播黑客文化,分享黑客技术精华

通过DIVA了解APP安全问题

2017-02-15 17:05

导语

篇幅有些长,请使用目录品用。

目录

  • 不安全日志输出
  1. Insecure Logging
  • 不安全的数据存储
  1. Insecure Data Storage -Part1
  2. Insecure Data Storage -Part2
  3. Insecure Data Storage -Part3
  4. Insecure Data Storage -Part4
  • 硬编码
  1. Hardcoding Issues -Part1
  2. Hardcoding Issues -Part2
  • 访问控制
  1. Access Control lssues -Part 1
  2. Access Control lssues -Part 2
  3. Access Control lssues -Part 3
  • 用户数据问题
  1. Input Validation Issues – Part1
  2. Input Validation Issues – Part2
  3. Input Validation Issues – Part3

DIVA

DIVA (Damn insecure and vulnerable App),是一个故意设计为全身漏洞的APP软件,它能让开发人员、QA、安全人员了解到APP软件一般存在的问题。

github地址:https://github.com/payatu/diva-android

不安全日志输出

开发人员有意或无意地记录敏感信息(如凭据,会话ID,财务详细信息等)

Insecure Logging

20161201124328103

我们输入的数字”123456789”,使用adb logcat 命令可以看到

E/diva-log( 3575): Error while processing transaction with credit card: 123456789

使用jd-gui看下logActivity.class 可以发现

Log.e("diva-log", "Error while processing transaction with credit card: " + paramView.getText().toString());
Toast.makeText(this, "An error occured. Please try again later", 0).show();

代码中使用了log.e,如果开发人员在debug时不小心忘记去掉log输出,将会造成很大的安全隐患。

不安全的数据存储

不安全的数据存储也是App常见的安全问题之一,主要有三种方式:

  • 将敏感数据保存到配置文件中。
  • 将敏感数据保存在本地的sqlite3数据库中。
  • 将敏感数据保存在临时文件或者sd卡中。

Insecure Data Storage -Part1

使用了SharedPreferences类,该类是Android平台上一个轻量级的存储类,主要是用来保存一些常用的配置,本例中是用该类存储了用户名和密码,因此是具有风险的。SharedPreferences类存储的数据会以.xml的形式存储在/data/data/apppackagename/shared_prefs目录下

输入用户名和密码都为admin

20161201141134152

使用adb连接手机查看文件

20161201141336283

InsecureDataStorage1Activity.class

paramView = PreferenceManager.getDefaultSharedPreferences(this).edit();
EditText localEditText1 = (EditText)findViewById(2131493000);
EditText localEditText2 = (EditText)findViewById(2131493001);
paramView.putString("user", localEditText1.getText().toString());
paramView.putString("password", localEditText2.getText().toString());
paramView.commit();
Toast.makeText(this, "3rd party credentials saved successfully!", 0).show();

Insecure Data Storage -Part2

一般app对应的数据库目录: /data/data/apppackagename/databases

输入用户名,密码都为ceshi

20161201142523905

使用adb查看数据库

20161201142439537

InsecureDataStorage2Activity.class

SQLiteDatabase localSQLiteDatabase = this.mDB;
paramView = new java/lang/StringBuilder;
paramView.<init>();
localSQLiteDatabase.execSQL("INSERT INTO myuser VALUES ('" + localEditText1.getText().toString() + "', '" 
+ localEditText2.getText().toString() + "');");
this.mDB.close();
Toast.makeText(this, "3rd party credentials saved successfully!", 0).show();

Insecure Data Storage -Part3

输入用户名和密码为demo

20161201143317059

使用adb查看临时文件,目录在/data/data/apppackagename/

20161201143358357

InsecureDataStorage3Activity.class

Object localObject2 = File.createTempFile("uinfo", "tmp", (File)localObject1);
((File)localObject2).setReadable(true);
((File)localObject2).setWritable(true);
localObject1 = new java/io/FileWriter;
((FileWriter)localObject1).<init>((File)localObject2);
localObject2 = new java/lang/StringBuilder;
((StringBuilder)localObject2).<init>();
((FileWriter)localObject1).write(localEditText.getText().toString() + ":" + paramView.getText().toString() + "\n");
((FileWriter)localObject1).close();
Toast.makeText(this, "3rd party credentials saved successfully!", 0).show();

Insecure Data Storage -Part4

存储sd卡的目录一般在: /mnt/sdcard

InsecureDataStorage4Activity.class

paramView = (EditText)findViewById(2131493010);
EditText localEditText = (EditText)findViewById(2131493011);
File localFile = Environment.getExternalStorageDirectory();
try
{
Object localObject1 = new java/io/File;
Object localObject2 = new java/lang/StringBuilder;
((StringBuilder)localObject2).<init>();
((File)localObject1).<init>(localFile.getAbsolutePath() + "/.uinfo.txt");
((File)localObject1).setReadable(true);
((File)localObject1).setWritable(true);
localObject2 = new java/io/FileWriter;
((FileWriter)localObject2).<init>((File)localObject1);
localObject1 = new java/lang/StringBuilder;
((StringBuilder)localObject1).<init>();
((FileWriter)localObject2).write(paramView.getText().toString() + ":" + localEditText.getText().toString() + "\n");
((FileWriter)localObject2).close();
Toast.makeText(this, "3rd party credentials saved successfully!", 0).show();
return;
}

可以看到使用Environment.getExternalStorageDirectory()来获取sd卡的目录,文件名为.unifo.txt

20161201144251242

硬编码

  • 某些需要比较字符串的值为硬编码,如:激活码
  • 加密的key或者salt为硬编码,如MD5的salt

Hardcoding Issues -Part1

这里直接查看源码就知道答案

20161201144746717

HardcodeActivity.class

if (((EditText)findViewById(2131492987)).getText().toString().equals("vendorsecretkey")) {
Toast.makeText(this, "Access granted! See you on the other side :)", 0).show();
}
for (;;)
{
return;
Toast.makeText(this, "Access denied! See you in hell :D", 0).show();
}

Hardcoding Issues -Part2

源码Hardcode2Activity.class

20161201145414023

这里使用了DivanJni类,查看 DivaJni.class

20161201145427383

这里加载了divajini库,一般库文件都放在/lib下,在目录下看到了很多架构,随便查看一个

  • arm64-v8a:

libdivajni.so

  • armeabi:

libdivajni.so

  • armeabi-v7a:

libdivajni.so

  • mips:

libdivajni.so

  • mips64:

libdivajni.so

  • x86:

libdivajni.so

  • x86_64:

libdivajni.so

Linux下可以使用strings方便的查看二进制文件里的字符串

$ strings libdivajni.so

/system/bin/linker

__cxa_finalize

__cxa_atexit

Java_jakhar_aseem_diva_DivaJni_access

strncmp

__aeabi_unwind_cpp_pr1

Java_jakhar_aseem_diva_DivaJni_initiateLaunchSequence

strcpy

__aeabi_unwind_cpp_pr0

JNI_OnLoad

__aeabi_unwind_cpp_pr2

__gnu_Unwind_Find_exidx

__gnu_Unwind_Restore_VFP_D

__gnu_Unwind_Restore_VFP

__gnu_Unwind_Restore_VFP_D_16_to_31

__gnu_Unwind_Restore_WMMXD

__gnu_Unwind_Restore_WMMXC

abort

restore_core_regs

memcpy

_Unwind_GetCFA

__gnu_Unwind_RaiseException

__gnu_Unwind_ForcedUnwind

__gnu_Unwind_Resume

__gnu_Unwind_Resume_or_Rethrow

_Unwind_Complete

_Unwind_DeleteException

_Unwind_VRS_Get

_Unwind_VRS_Set

__gnu_Unwind_Backtrace

__cxa_begin_cleanup

__cxa_type_match

__gnu_unwind_execute

__cxa_call_unexpected

_Unwind_VRS_Pop

__gnu_Unwind_Save_VFP_D

__gnu_Unwind_Save_VFP

__gnu_Unwind_Save_VFP_D_16_to_31

__gnu_Unwind_Save_WMMXD

__gnu_Unwind_Save_WMMXC

__restore_core_regs

___Unwind_RaiseException

_Unwind_RaiseException

___Unwind_Resume

_Unwind_Resume

___Unwind_Resume_or_Rethrow

_Unwind_Resume_or_Rethrow

___Unwind_ForcedUnwind

_Unwind_ForcedUnwind

___Unwind_Backtrace

_Unwind_Backtrace

__gnu_unwind_frame

_Unwind_GetRegionStart

_Unwind_GetLanguageSpecificData

_Unwind_GetDataRelBase

_Unwind_GetTextRelBase

_edata

__bss_start

_end

libstdc++.so

libm.so

libc.so

libdl.so

libdivajni.so

olsdfgad;lh

.dotdot

GCC: (GNU) 4.8

gold 1.11

aeabi

.shstrtab

.interp

.dynsym

.dynstr

.hash

.rel.dyn

.rel.plt

.text

.ARM.extab

.ARM.exidx

.rodata

.fini_array

.init_array

.dynamic

.got

.data

.bss

.comment

.note.gnu.gold-version

.ARM.attributes

看里面又没特别的字符串,就像是olsdfgad;lh,试一下成功了

20161201145610008

在下面链接看源代码就可以知道,vendorkey是硬编码。

https://github.com/payatu/diva-android/blob/master/app/src/main/jni/divajni.c

20161201145637683

访问控制

Access Control lssues -Part 1

20161201150001932

我们可以按下面的按钮获取到API 凭据:

20161201150029057

我们的目标是不使用按钮就获取到API凭据,查看 AndroidManifest.XML文件,查看和供应商API凭据提供相关的activity:

<activity android:label=”@string/apic_label” android:name=”jakhar.aseem.diva.APICredsActivity”>

<intent-filter>

<action android:name=”jakhar.aseem.diva.action.VIEW_CREDS”/>

<category android:name=”android.intent.category.DEFAULT”/>

</intent-filter>

</activity>

通过观察代码发现,这个activity被intent filter所“保护”,当intent filter被activity等组件使用,这个activity可能会被外部任何应用程序所调用。

我们可以使用下面的命令:

$ adb shell am start jakhar.aseem.diva/.APICredsActivity
  • adb shell: 进入手机shell模式
  • am: activity 管理工具
  • start: 启动activity

20161201150330022

接着手机就弹出了之前的页面

20161201150340507

上面的命令还有另一种写法:

$ adb shell am start -n jakhar.aseem.diva/.APICredsActivity -a jakhar.aseem.diva.action.VIEW_CREDS
  • -a:指定action
  • -n:指定完整 component 名

Access Control lssues -Part 2

20161201150707059

如objective所知,如果你已经注册,你就能拥有tveeter API Credentials,当前的挑战是,不注册来获取API Credentials。

和之前一样查看 AndroidManifest.XML文件:

<activity android:label=”@string/apic2_label” android:name=”jakhar.aseem.diva.APICreds2Activity”>

<intent-filter>

<action android:name=”jakhar.aseem.diva.action.VIEW_CREDS2″/>

<category android:name=”android.intent.category.DEFAULT”/>

</intent-filter>

</activity>

试下使用老套路:

$ adb shell am start -n jakhar.aseem.diva/.APICreds2Activity -a jakhar.aseem.diva.action.VIEW_CREDS2

20161201150717560

可以看到我们跳入了选择未注册时相同的activity,这表明程序做了相应的措施,我们看下源代码

  • APICreds2Activity.class

20161201150729294

从Intent中获取到上个activity传来的boolean值(2131099686),当这个值为false时就视为用户已注册,再看看上个activity。

  • AccessControl2Activity.class

20161201150737544

这个类中bool值是通过单选项来决定的,而且把值传给下个activity。这时我们可以使用–ez来传递一个boolean键值对,还要获取到2131099686值对应的key值是什么,一般情况下使用apktool反编译的字符串文件存放在res/vaules/strings.xml文件下,string文件中所有的字符串资源都会被R.Java的String类中被标识,每个字符串中都有唯一的iint类型索引值,在反编译的情况下所有的索引值都保存在strings.xml同目录下的public.xml文件中,所以,我们现在public中查看16进制的2131099686(0x7f060026 )对应的name值

<public type="string" name="chk_pin" id="0x7f060026" />

然后再strings文件下查找chk_pin,最后得到key 为check_pin。

<string name="chk_pin">check_pin</string>

最后的命令:

$ adb shell am start -a jakhar.aseem.diva.action.VIEW_CREDS2 -n jakhar.aseem.diva/.APICreds2Activity --ez 
check_pin false

20161201151203156

Access Control lssues -Part 3

20161201151401005

这是个私人的笔记app,一开始需要设置密码才能使用,我们的目标是不设置密码就开始使用。

查看 AndroidManifest.XML文件:

<provider android:authorities=”jakhar.aseem.diva.provider.notesprovider” android:enabled=”true” 
android:exported=”true” android:name=”jakhar.aseem.diva.NotesProvider”/>

这里使用了ContentProvider,android:enabled表示是否能由系统初始化,android:exported表示是否能被其他应用使用,android:authorities:标识这个ContentProvider,调用者可以根据这个标识来找到它,具体内容可以查阅其他资料。

看到2个值都为true,我们就可以使用content://访问里面的数据了,搜索包含content://的字符串文件:

  • smali/jakhar/aseem/diva/NotesProvider.smali

20161201151412443

找到后和我们在AndroidManifest所看到的一样,我们可以使用以下命令随意的访问该uri:

$ adb shell content query –-uri content://jakhar.aseem.diva.provider.notesprovider/notes

20161201151420349

就如我们设置密码进入后所看到的信息一样。

用户数据问题

从外部输入的组件获取到用户输入的数据后,在使用前应该进行检验长度和过滤等操作。

Input Validation Issues – Part1

这里面总共有3个用户的数据

20161201151920652

当输入’时,没有任何返回,查看logcat会发现:

20161201151927902

出现了sql注入,我们输入1’or ‘1’=’1时

20161201151937816

所有用户数据都出来了,查看下对应的SQLInjectionActivity.class 源代码:

20161201151945785

我们会发现开发人员并没有对用户的输入数据进行处理。

Input Validation Issues – Part2

直接输入http://www.baidu.com 可以看到打开了百度的页面

20161201152251985

如果将http协议换成File协议(File协议主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样),那我们就可以读取敏感信息了。查看下之前存储在sd卡的账号文件,file:///mnt/sdcard/.uinfo.tx

20161201152259005

这样我们就能读取手机内其他的文件信息了,查看下对应的 InputValidation2URISchemeActivity.class源码:

20161201152312349

可以发现同样是没有对用户数据进行处理。

Input Validation Issues – Part3

20161201152452582

简单输入AAAA,出现了正常的反应。

20161201152459350

试下输入超级长的AAAA…就会发现activity会直接崩溃,查看logcat就能发现缓冲区溢出了。

20161201152506615

查看源码

https://github.com/payatu/diva-android/blob/master/app/src/main/jni/divajni.c

20161201152512631

使用的是strcpy,典型的字符串复制溢出。

参考来源

 

*本文由作者:Gintoki 原创投稿,未经允许禁止转载

知识来源: www.mottoin.com/95379.html
想收藏或者和大家分享这篇好文章→复制链接地址

“通过DIVA了解APP安全问题”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

关注公众号hackdig,学习最新黑客技术

推广

工具

标签云

本页关键词