EYEN
DIVA 11단계 본문
#1 문제 분석
가. 목적
앱 외부에서 private note 에 pin없이 접근해보자
#2 취약점 정보
가. 취약점
provider로 감싼 부분에서 notesprovider는 exported=true라서 외부에서 경로만으로 접근가능하다.
앱 밖에서 PIN을 모른채 private note에 접근하세요.
근데 왜 am start -d jakhar.aseem.diva.provider.notesprovider/notes는 이렇게 뜰까??
아 am start는 어떤 액티비티를 시작하는 거라서, 시작할 때의 어떤 값을 정해주는 거고
content query --uri 는 db내의 컨텐트 프로바이더를 쿼리하기 위한 것이다. --uri옵션은 쿼리할 컨텐트 프로바이더의 URI를 지정한다.
pin을 알고 접속하는 법은 없음 이건 db안에 존재하는거라 10번처럼 액티비티로 접속할 방법이 없음
#3 코드 분석
1.
String pin = spref.getString(getString(R.string.pkey), "");
SharedPreferences 객체에서 문자열 값을 가져옴
2.
Cursor cr = getContentResolver().query(NotesProvider.CONTENT_URI, new String[]{"_id", "title", "note"}, null, null, null);
NotesProvider에서 "_id", "title", "note" 열을 반환하는 쿼리를 실행하고, 결과를 Cursor 객체인 cr에 저장
3.
String[] columns = {"title", "note"};
int[] fields = {R.id.title_entry, R.id.note_entry};
어댑터는 데이터와 뷰 간의 매핑을 담당하며, 이러한 배열을 사용하여 데이터를 표시할 때 각 열에 대해 어떤 뷰를 사용할지 결정
"title" 열의 값은 R.id.title_entry에 해당하는 뷰에 표시될 것이고, "note" 열의 값은 R.id.note_entry에 해당하는 뷰에 표시될 것
4.
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.notes_entry, cr, columns, fields, 0);
SimpleCursorAdapter: CursorAdapter의 하위 클래스, Cursor 객체에서 데이터를 가져와 AdapterView의 각 항목을 채우는 역할
cr: 쿼리된 결과를 담고 있음
0: CursorAdapter가 데이터를 로드할 때 추가적인 조작을 수행하지 않도록 함
5.
lview.setAdapter((ListAdapter) adapter);
setAdapter(): AdapterView에 어댑터를 설정한다. SimpleCursorAdapter를 ListAdpter로 형변환해 전달
6.
pinTxt.setVisibility(4);
뷰를 인식할 수 없음으로 설정한다.
7.
static final Uri CONTENT_URI = Uri.parse("content://jakhar.aseem.diva.provider.notesprovider/notes");
static final UriMatcher urimatcher = new UriMatcher(-1);
Uri.parse: 문자열 형태의 URI르 uri객체로 변환한다.
UriMatcher: URI패턴을 매칭하여 처리할 액션을 결정하는 데 사용한다. -1은 초기화할 때 어떤 매치도 하지않음을 의미한다.
8.
static {
urimatcher.addURI(AUTHORITY, TABLE, 1);
urimatcher.addURI(AUTHORITY, "notes/#", 2);
}
URI패턴과 해당하는 매치 코드를 추가한다.
AUTHORITY는 컨텐트 프로바이더의 인증 정보, 주소 패키지 이름, TABLE은 DB의 테이블 이름, notes라는 테이블을 가리킨다.
1은 테이블의 모든 데이터, notes/#은 특정 순서의 데이터
9.
String id = uri.getLastPathSegment();
count = this.mDB.delete(TABLE, "_id = " + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
삭제할 레코드의 id로 설정하기 위해 uri의 마지막 세그먼트를 가져온다.
특정 조건에 맞는 레코드를 삭제한다.
10.
public String getType(Uri uri) {
switch (urimatcher.match(uri)) {
case 1:
return "vnd.android.cursor.dir/vnd.jakhar.notes";
case 2:
return "vnd.android.cursor.item/vnd.jakhar.notes";
default:
throw new IllegalArgumentException("Divanotes: Unsupported URI: " + uri);
}
}
매치코드가 1이냐 2냐
11.
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row);
ContentUris.withAppendedId() 메서드를 사용하여 기본 URI인 CONTENT_URI와 삽입된 레코드의 행 번호를 결합합니다.
12.
Cursor cursor = queryBuilder.query(this.mDB, projection, selection, selectionArgs, null, null, sortOrder);
queryBuilder를 사용하여 데이터베이스에서 쿼리를 수행 하고, Cursor 객체로 반환된다.