1. FridaLab 이란?
FridaLab은 frida를 통해 분석할 수 있는 취약점 테스트 어플리케이션이다.
FridaLab
FridaLab 문제풀이
이제 어플리케이션에 접속하여 어떤 문제가 포진되어있는지 확인한다.
프리다를 접속하면 Challenge가 8번까지 있는 것을 확인할 수 있다.
이 후, jadx를 사용하여 소스코드를 확인할 수 있도록 설정한다.
Challenge 1
Change class challenge_01's variable 'chall01' to 1
첫 번째 문제는 challenge_01 클래스의 변수 chall01 을 1 로 바꿔야한다.
challenge_01의 코드는 위와 같다.
여기서 static 변수 chall01 을 1로 변경을 해야하는 후킹시도를 진행합니다.
setImmediate(function(){
Java.perform(function(){
var chall_01 = Java.use("uk.rossmarks.fridalab.challenge_01");
chall_01.chall01.value = 1;
console.log("Challenge 1 success")
})
})
Java.use
Static 변수 및 메소드는 프로그램 실행 시 메모리에 자동으로 올라오므로 인스턴스를 호출할 필요가 없다.
이때는 Java.use를 사용해서 후킹한다.
특정 Java 클래스를 대상으로 해당 클래스의 참조를 가져와 클래스의 메서드나 속성을 후킹하거나 조작를 진행한다.
이제 프리다에서 작성한 js 파일을 FridaLab에 실행시켜보도록 한다.
# 앱 이름으로 지정
frida -U -l fridalab1.js FridaLab
# 패키지 명으로 지정
frida -U -l fridalab.js -f uk.rossmarks.fridalab --no-pause
Frida 15버전 부터는 실행되는 프로세스 명을 패키지명이 아닌 앱 이름으로 변경해서 사용한다.
패키지명을 지정할 경우, spawn으로 하면 된다.
하단에 Challenge 1 success 가 출력되며, 성공적으로 변수 값을 변경한 것을 확인할 수 있다.
Challenge 2
Run chall02()
두 번째 문제는 chall02() 메서드를 실행시켜야 한다.
위의 그림과 같이 chall02 메서드의 사용을 확인할 수 있다.
여기서 chall02 가 실행되도록 후킹을 시도한다.
setImmediate(function(){
Java.perform(function(){
Java.choose("uk.rossmarks.fridalab.MainActivity",
{
"onMatch" : function(chall_02){
chall_02.chall02();
},
"onComplete" : function(){
console.log("\n Challenge 02 success")
}
})
})
})
Java.choose
런타임에서 특정 클래스의 인스턴스들을 열거하는 데 사용된다.
메모리에서 이미 생성된 객체 인스턴스를 찾아낼 때 유용하다.
이제 판별의 기준은 static method일 경우 제외이며, static 이외의 메서드들은 인스턴스 메서드 라고 보면 된다.
정상적으로 chall02가 실행되는 것을 확인할 수 있다.
Challenge 3
Make chall03() return true
세 번째 문제는 chall03() 메서드의 리턴 값을 true로 만들라고 한다.
Chall03이 어떻게 구성되어있는지 확인하였으니, 이제 값을 변경할 차례이다.
setImmediate(function(){
Java.perform(function(){
var chall_03 = Java.use("uk.rossmarks.fridalab.MainActivity");
chall_03.chall03.implementation = function(){
console.log("Challenge 3 success")
return true;
}
})
})
java.use를 사용해 클래스 객체에 접근하여 내부 값을 변경시킨다.
chall03.implementation 는 함수를 재작성하기 위해 사용한다.
Challenge 4
Send "frida" to chall04()
네 번째 문제는 chall04()에 "frida" 문자열을 전송해라.
기존의 메서드의 인스턴스를 사용하여 후킹을 진행한다.
setImmediate(function(){
Java.perform(function(){
Java.choose("uk.rossmarks.fridalab.MainActivity", {
onMatch : function(chall_04){
chall_04.chall04("frida");
},
onComplete : function(){
console.log("Challenge 04 success");
}
})
})
})
Challenge 5
Always send "frida" to chall05()
다섯 번째 문제는 항상 "frida" 문자열을 chall05() 함수에 보내는 문제이다.
항상 "frida" 문자열을 보내기 위해서는 함수를 변경해야한다.
setImmediate(function(){
Java.perform(function(){
var chall_05 = Java.use("uk.rossmarks.fridalab.MainActivity");
chall_05.chall05.overload("java.lang.String").implementation = function(arg){
this.chall05("frida");
console.log("Challenge 05 success")
}
})
})
위의 코드를 보자
- 함수를 변경하기위해 Java.use 를 사용한다.
- chall_05 변수에 클래스 객체를 담아 위그림의 chall05 함수를 overload 함수를 사용해 String 클래스를 인자로 받는 다는 것을 의미한다.
- implementation 을 사용해 함수의 재정의를 하며, function에 arg 인자를 받는 다는 것을 명시해준다.
- arg는 String 인자를 받는다 라고 생각하면 된다.
- this.chall05("frida") 를 명시하여 chall05 함수에서 "frida" 문자열을 항상 받게끔 직접 작성한다.
Challenge 6
Run chall06() after 10 seconds with correct value
여섯 번째 문제는 올바른 값으로 10초 후 chall06()을 실행합니다.
Chall06(int i) 내부에는 challenge_06 클래스 내부에 있는 confirmChall06의 값이 true 가 나오도록 해야한다.
고로 challenge06.class 를 확인한다.
이제 아래와 같이 후킹을 시도한다.
setImmediate(function() {
Java.perform(function() {
console.log("10sec Challenge 06");
var challenge06 = Java.use("uk.rossmarks.fridalab.challenge_06");
Java.choose("uk.rossmarks.fridalab.MainActivity", {
onMatch: function(instance) {
challenge06.addChall06.implementation = function (i) {
challenge06.timeStart.value = -10000;
this.addChall06(i);
};
instance.chall06(Number(challenge06.chall06.value));
},
onComplete: function(instance) {}
});
console.log("\nChallenge 06 success")
});
});
- setTimeout 을 사용해서 10초 뒤에 함수가 작동하도록 설정한다.
- MainActivity 의 chall06 함수 내부에는 단순히 challenge_06.class 의 confirmChall06을 사용하므로 class로 이동하여 코드를 확인한다.
- challenge_06.class 에는 chall06의 작동 시간과 설정 값이 포함되어 있다. class 객체에서 함수를 재정의하기 위해 Java.use 를 사용한다.
- 이제 함수의 재정의 내부에서 작동하는 인스턴스인 MainActivity의 chall06 메서드의 인자 값을 chall06의 값으로 받는다 로 설정한다.
Challenge 7
Bruteforce check07Pin() then confirm with chall07()
일곱 번째 문제는 무차별대입을 실행해야 한다.
위의 코드를 분석하여 코드를 작성한다.
Java.perform(function(){
var chall_07 = Java.use("uk.rossmarks.fridalab.challenge_07");
Java.choose("uk.rossmarks.fridalab.MainActivity", {
"onMatch" : function(instance){
for(var i=9999; i>=1000; i--){
var str_i = String(i);
if(chall_07.check07Pin(str_i)){
instance.chall07(str_i);
break;
}
}
},
"onComplete" : function(){
console.log("Challenge 07 success")
}
})
})
- chall07이 작동하는 로직에서 정상적으로 작동의 확인을 위해 Challenge07의 CheckPin 함수를 사용하기 위해 Java.use를 선언한다.
- MainActivity에서 작동하는 프로그램에 직접대입해서 넣기위해, instance 를 불러온다.
- for 문으로 로직을 작성하고, check07Pin은 str 자료형으로 받기에 자료형을 맞춰준다.
- chall_07 클래스 객체의 check07Pin 메소드를 가져와 str 자료형으로 맞춰준 str_i를 넣어준다.
- if 문에 true 가 출력되면 break; 되도록 설정한다.
지금도 작성이 되지만, 설정되어 있는 번호의 값을 확인하기 위해 아래의 코드를 추가한다.
#if문 바로 아래에 추가하면 된다.
console.log("pwd :"+str_i)
위의 코드를 추가하면, 콘솔에서 직접 확인이 가능하다.
Challenge 8
Change 'check' button's text value to 'Confirm'
여덟 번째 문제는 'check' 문자열로 되어있는 버튼은 'Confirm'으로 바꾸는 것을 말한다.
위의 코드를 확인하고 코드를 작성한다.
Java.perform(function(){
var klass = Java.use("android.widget.Button");
Java.choose("uk.rossmarks.fridalab.MainActivity", {
"onMatch" : function(instance){
var checkid = instance.findViewById(0x7f07002f);
var check = Java.cast(checkid, klass);
check.setText("Confirm");
},
"onComplete" : function(){
console.log("Challenge 08 success")
}
})
})
- (Button) 으로 타입을 설정하기에 Java.use 에서 클래스를 import 받는 android.widget.Button 을 klass 변수로 설정한다.
- Java.choose에서 MainActivity를 가져오고 checkid에서는 기존의 check 값을 입력한다.
- (Button) 을 동일하게 설정해 주기위해 Java.cast(checkid, klass)로 사용한다.
- (Button) 형태에서 이제 setText를 사용해 "Confirm" 문자열을 넣어준다.
하지만 위의 코드는 에러가 발생한다.
위 그림에서 설명하듯 setText() 함수에서 사용할 수 있는 인자의 타입을 보여준다.
여기에서는 java.lang.CharSequence 로 맞춰줘야 한다는 에러가 발생한다.
java.lang.String 을 사용하면 java.lang.CharSequence 로 돌려줄 수 있는 역할을 하고 있다.
Java.perform(function(){
var klass = Java.use("android.widget.Button");
Java.choose("uk.rossmarks.fridalab.MainActivity", {
"onMatch" : function(instance){
var checkid = instance.findViewById(0x7f07002f);
var check = Java.cast(checkid, klass);
var string = Java.use("java.lang.String");
check.setText(string.$new("Confirm"));
},
"onComplete" : function(){
console.log("Challenge 08 success")
}
})
})
중간에 코드를 추가한다.
Java.lang.String class에 대한 객체를 String 에 담고 해당 string을 이용한다.
string.$new() 는 Java.use로 선언한 클래스로 반환된 객체를 $new() 가 선언된 장소에 인스턴스화 해준다. 선언만 되어있을 경우 변경이 안되기 때문에, $new("Confirm")를 선언하여 java.lang.String으로 만들어준다.
이제 실행해보자
succes 구문이 출력되며, 이제 frida 앱을 확인해본다.
Check 에서 Confirm 으로 변경되어 있는 것을 확인할 수 있다.
[출처] 인프런 보안프로젝트 프리다(Frida)를 이용한 안드로이드 앱 모의해킹
'앱 모의해킹 > 안드로이드' 카테고리의 다른 글
Frida(2) - 후킹, 쉘 다루기 (0) | 2024.06.14 |
---|---|
Frida(1) - 문법, 개념 이해 (0) | 2024.06.13 |
InsecureBank 취약점 분석 (0) | 2024.06.04 |
Drozer 프레임워크 (0) | 2024.05.30 |
안드로이드 기본개념(3) (0) | 2024.05.29 |