1. Cycript

Cycript는 iOS에서 실행되고 있는 애플리케이션을 동적으로 수정하고 분석을 할 수 있게 해주는 SDK이다.

문법은 기본적으로 Objective-C / JavaScript를 혼합하여 사용 가능하다.

 


2. Cycript Hooking


2-1) view계층 확인하기

ex) UIApp.keyWindow.recursiveDescription()


view의 계층을 출력하여준다. NSLog로 출력하여 큰 화면으로 보면 아래와 같이 이쁘게 보인다.



2-2) subviews()

ex) UIApp.keyWindow.subviews()[index]


window는 하나 이상의 view를 가지며, view 역시 하나 이상의 view의 집합이다. 각 하위 view들의 접근은 subviews()[index]의 형태로 접근 가능하고, 2-1에서 확인한 계층을 참조하여 각 요소들을 찾을 수 있다.



2-3) _viewDelegate()

ex) UIApp.keyWindow.subviews()[2].subviews()[0]._viewDelegate()


_viewDelegate를 이용하여 해당 view의 controller에 접근 가능하다.


2-4) replacing existing objective-C methods

ex) Game2ViewController.prototype['recognizeAnswer'] 

= function() { NSLog(@"[JSACH]HOOKED"); return 1;}


target ViewController를 찾아 prototype배열에 후킹할 method에 접근하여 위와 같이 재정의해줌으로써 replacing가능함



참고) Cycript에서 사용하는 API나 문법, Trick들이 정리되어있는 링크

 

 - iOS Tricks

     http://iphonedevwiki.net/index.php/Cycript_Tricks


 - Cycript공식 홈페이지

http://www.cycript.org/


참고로 이 글은 Cycript 0.9.594버전을 기준으로 작성을 하였다.



3. 결과


3-1) 메서드 재정의


1
Game2ViewController.prototype['recognizeAnswer'= function() { NSLog(@"[JSBACH]HOOKED"); NSLog(@"[JSBACH] return SUCCESSED"); return 1;}





3-2) 메서드 후킹 로그


3-3) 후킹 성공 결과




4. 후기

frida와 Logos 그리고 오늘 Cycript까지 정리하면서 처음에 계획했던 후킹 방법 세가지에 대해 모두 간단하게 겪어(?)보았다. 각 방법의 장단점이 어느 정도 감이 오는 것 같아서 매우 뿌듯하고 흡족스럽다!


개인적으로 생각하는 frida와 Logos, Cycript를 비교하면 이렇다고 생각한다.


 항목

Logos 

Frida 

Cycript 

설치의 편의성

 

 

상 

수정사항 반영의 편의성

 

 

상 

후킹 사항 유지력 

 

하 

중 

 참조 자료의 양

중 

상 


4-1) 설치의 편의성

후킹하기 위한 각 툴들의 환경설치에 대한 평가 항목으로 선정해보았고, 개인적으로 소요된 시간과 환경의 까탈스러움 등을 고려하여 평가해보았다.


Logos의 경우 Mac사용자라면 간편한 편일 수 있으나, 본인은 Kali Linux에 환경을 설치하면서 꽤나 애를 먹었던 기억이 나서 낮은 '하'로 선정하였다.


Frida는 비교적 편리하게 설치하였으나, 단말에 Server Binary를 올려야하는 점과 Cycript만큼 간단하지 않았던 것같아(ㅎㅎ) '중'으로 점수를 매겼다.


Cycript의 경우 Cycia에서 패키지 설치만으로 모든 설치가 정상적으로 완료 되어 '상'으로 책정하였다.



4-2) 수정사항 반영의 편의성

후킹 코드 작성 중 수정사항이 생긴경우 재 반영하는 방법의 편의성에 대한 평가 항목이다.


Logos는 .deb파일을 재설치하고 Spring Board가 재시동되어야 하는 불편함이 있어 '하'가 되었다.


Frida의 경우 script를 종료 시킨 후 수정, 재실행의 과정이 있어야 하고.. 아무래도 Cycript만큼 간단하지 않았던것 같아 '중'으로 하였다.


Cycript는 후킹 script만 재정의 해주면 되므로 가장 간단하다고 판단하여 '상'으로 하였다.


4-3) 후킹 사항 유지력(?)

항목이름이 이상한거 같긴한데.. 적절한 명칭이 떠오르지않아서ㅠ... 한번 후킹 코드를 넣었을 때, 얼마나 유지가 되는지(?)에 대한 항목이다.


Logos는 한번 .deb설치가 되면 삭제가 될때까지 유지가 된다. 즉, 개발한 패키지를 한번 설치를 하고난 후 특별한 조치를 취하지 않아도 후킹코드가 유지된다. 가장 유지력이 좋다고 생각하여 '상'으로 책정하였다.


Frida는 후킹 script가 종료되기 전까지 유지가 되고, Cycript는 후킹 Process가 종료되기 전까지 유지 되므로 각각 '하'와 '중'으로 하였다.


4-4) 참조 자료의 양

비교 항목 중 가장 주관적인 항목으로 본인이 검색해 본 경험에 따라 그냥 매겼다.

아무래도 요즘 많이 떠오르고있는 Frida가 자료가 가장 많아 '상', Cycript와 Logos는 비슷 한것 같지만

Cycript가 예전 자료가 너무 많이나와서 사실상 볼 만한 자료가 거의 없었다. 따라서 더 애를 많이 먹었던 것 같아 '하'로 책정하였다.





다음 목표는 Logos를 이용하여 tsprotector와 비슷한 JB우회 패키지를 만들어 보고자 한다. 

fopen과 같은 file관련 API들, System call등을 후킹하여야 할 것으로 예상된다~


'Study > iOS' 카테고리의 다른 글

iOS Hooking#3(Cycript)  (2) 2017.05.06
iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13
  1. 1357 2017.05.10 13:48

    유용한 정보 잘보고갑니다 ~ 후킹 ~~ 후~킹~

  2. 안녕하셒 2017.05.10 13:50

    좋은 내용이네요. 잘 봤습니다. :]

1. Frida

Inject JavaScript to explore native apps on Windows, macOS, Linux, iOS, Android, and QNX.

[출처] https://www.frida.re/


Frida는 자바스크립트를 이용하여 Windows, macOS, Linux, iOS, Android와 QNX와 같은 다양한 플랫폼에서 후킹을 할 수 있는 플랫폼이다. 파이썬 틀에 인젝션할 코드를 자바스크립트로 작성하여 파이썬으로 실행 할 수 있다. 



2. Frida Hooking

iOS Hooking#1 에서 분석한 동일한 앱을 대상으로 진행을 하였으므로 앱 분석은 생략한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# -*- conding: utf-8 -*-
import frida
import sys 
 
PACKAGE_NAME = "kr.history"
 
#send실행 시 출력할 format정의
def on_message(message, data):
    try:
        if message:
            print("[JSBACH] {0}".format(message["payload"]))
    except Exception as e:
        print(message)
        print(e)
 
 
def do_hook():
 
    hook = ""
    //Objective-C가 실행 가능한 환경인지 검사
    if(ObjC.available){
        //해당 attach된 프로세스의 메모리에 올라가있는 클래스들을 가져올 수있고,
        //아래 for문은 타겟 클래스가 존재하는지 검사했다.
        for(var className in ObjC.classes){
            if(className == "Game2ViewController"){
                send("Found our target class : " + className);
            }
        }
    
        //Hooking을 진행할 메서드 객체를 가져온다.
        var hook_method = ObjC.classes.Game2ViewController["- recognizeAnswer"];
        send("print hook_method : " + hook_method);
        
        Interceptor.attach(hook_method.implementation, {
        
            //onEnter는 후킹함수 진입 시 실행되며, args[0]에는 self객체가
            //args[1]에는 selector객체가 들어있어 접근 가능하며
            //args[2]에는 해당 함수의 매개변수들이 들어있다.
            //매개변수를 변경하고 싶다면 이곳에서 변경한다.
            onEnter: function(args){
                
                var receiver = new ObjC.Object(args[0]);
                send("Target class : " + receiver.$className);
                send("Target superclass : " + receiver.$superClass.$className);
                var sel = ObjC.selectorAsString(args[1]);
                send("typeof sel = " + typeof sel);
                send("Hooked the target method : " + sel);
            },
            //onLeave는 함수 종료전 처리를 할 수 있다.
            //retVal에는 원래의 return값이 들어있고, return값을 변경하고 싶다면
            //이곳에서 변경한다.
            onLeave: function(retVal){
                //오답 시 리턴 값
                var wrong   = -1;
                //정답 시 리턴 값
                var correct = 1; 
                //retVal은 Object객체이며, int값으로 사용하기 위해 toInt32()를 사용
                var orig_rtn = retVal.toInt32();
                if(-1 == orig_rtn){
                    send("answer is Wrong!! : " + orig_rtn);
                    send("answer is replaced!!");
                    //값을 변경하기위해 replace()를 사용하여 변경함
                    retVal.replace(correct);
                }
                else{
                    send("answer is Correct!! : " + orig_rtn);
                }
            }
        });
    }
    //Objective-C 실행 환경이 아닌 경우 로그 출력
    else{
        console.log("Objective-C Runtime is not available!");
    }
    """
 
    return hook
 
if __name__ == '__main__':
 
    try:
        #연결할 단말을 찾는다.
        device = frida.get_device_manager().enumerate_devices()[-1]
 
        #타겟으로 할 앱의 패키지명으로 단말에서 실행되고 있는 pid를 가져온다.
        pid = device.spawn([PACKAGE_NAME])
        print("[JSBACH] {} is starting. (pid : {})".format(PACKAGE_NAME, pid))
 
        #위에서 얻어온 pid에 attach한다.
        session = device.attach(pid)
        device.resume(pid)
 
        #후킹코드를 injection한다.
        script = session.create_script(do_hook())
        script.on('message', on_message)
        script.load()
        sys.stdin.read()
    except KeyboardInterrupt:
        sys.exit(0)



코드에 대한 설명은 주석으로 대체합니다.


3. 결과





4. 코드 작성하며 발생했던 사소한 문제점들

4-1) frida client / server간 버전 충돌 문제

frida --version / frida-server --version 으로 확인한 메이저버전이 다를 경우 frida가 제대로 동작하지 않는다.


4-2) 32bit 단말 문제

실습에 활용한 단말은 iPhone 5로 32bit 단말이였는데, Frida최신버전(9.1.27)버전에서 제대로 훜이 걸리지않음

-> 8.2.2버전으로 낮추어 설치하여 정상적으로 동작 (pip install frida==8.2.2)


4-3) retVal 값 format

onLeave에있는 retVal값을 toString()을 이용해 사용하면 헥사 값이 나온다. (위 예제에서는 오답인 경우 0xffffffff, 정답인 경우 0x1) 따라서 toInt32()함수를 이용해서 int로만들어 주었다.

0xffffffff를 parseInt()를 사용하여 출력하면 unsigned int형의 최대값인 4294967295가 출력되었다.


4-4) onEnter

onEnter에서 args에 접근하여 receiver와 receiver의 superclass를 출력할 때, 많은 예제코드에서 

$className이 생략되어있었는데, 타입 변환 에러가 나면서 제대로 출력되지 않았으며, frida API를 참조해서 

$className을 추가하여 해결함

[참조] https://www.frida.re/docs/javascript-api/#objc


4. 후기

Logos와 비교했을 때, 후킹 코드의 빌드나 설치가 별도로 필요 없어서 가볍다는 느낌이 들었다.

python과 JavaScript로 iOS후킹 코드를 짤 수 있다는 게 신기했고,

탈옥상태가 아닌 폰에서도 후킹이 가능하다는 점도 신기했다.

'Study > iOS' 카테고리의 다른 글

iOS Hooking#3(Cycript)  (2) 2017.05.06
iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13

1. Logos

Logos is a component of the Theos development suite that allows method hooking code to be written easily and clearly, using a set of special preprocessor directives.


-[출처]http://iphonedevwiki.net/index.php/Logos


로고스는 Theos컴포넌트로 로고스를 이용하면 iOS 메소드 후킹 코드를 쉽고 깔끔하게 작성할 수 있다.

로고스를 이용한 후킹을 실습하기 위해 퀴즈앱을 대상으로 진행해보았다.


2. App Logic

2-1) 앱 실행 화면

앱은 퀴즈와 함께 해당 퀴즈 정답의 글자 수가 표시되고, 아래 글자를 클릭하여 정답을 맞추는 형식을 갖추고 있다.


2-2) iDA 정적 분석


Clutch를 이용하여 복호화한 Binary를 iDA로 열어 코드를 분석하면 위와 같은 코드를 발견 할 수 있는데, recognizeAnswer라는 메소드의 결과 값에 따라 congratulationViewVisible여부를 결정 하는 것을 알 수 있다.


recognizeAnswer메소드의 내용은 다음과 같다.


위 코드에서는 조금 잘렸는데, v2는 Game2ViewController의 self객체이고, answer_nospace의 길이가 0이 아니면 answer_nospace의 길이만큼 while문을 돌면서 userAnswer와 answer_nospace를 한글자씩 비교한다.

그 후 모두 매치하면 v15를 1로 만들고 매치하지 않으면 break한 후 v15를 -1로 만드게 되고 이 v15값을 리턴한다. 즉 유저의 정답과 문제의 정답을 비교하여 정답인 경우 1을 리턴하고, 오답인 경우 -1을 리턴하는 메서드임을 알 수 있다. 


2-3) Class-dump

2-2에서 확인한 Game2ViewController의 멤버변수나 메서드들을 Class-dump로 확인해 보았고, 우리가 주시해야하는 것들 부분만 발췌하였다.

answer_nospace는 앱에서 가지고있는 (분석한 결과 앱내 DB에서 조회해오는 값) 정답에 해당하는 변수이고, userAnswer는 유저가 입력한 답안 변수이다.



recognizeAnswer메서드는 멤버변수 값들로만 리턴 값을 정할 수 있으므로 특별한 Input값은 필요하지 않은 형태로 존재함을 확인 할 수있다.



3. Logos Hooking

위 분석 결과를 바탕으로 recognizeAnswer를 후킹하여 어떤 답안이 입력되든 정답처리가 될 수 있도록 Logos를이용한 후킹코드를 작성해 보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
  
@interface Game2ViewController : UIViewController{
  - (int)recognizeAnswer;
@end
%hook Game2ViewController
- (int)recognizeAnswer{
    int ret = %orig;
    int correct = 1;
    NSLog(@"[JSBACH]recognizeAnswer is Hooked\n"); 
    if(-1 == ret){
         NSLog(@"[JSBACH]answer is Wrong!! : %d", ret);
    }    
    else{
         NSLog(@"[JSBACH]answer is Correct!! : %d", ret);
    }
 
    return correct;
}
%end



@interface Game2ViewController : UIViewController{

}


- (int)recognizeAnswer;

@end


후킹 타겟으로 하는 클래스/인터페이스와 해당 메서드의 form을 정의해준다.

우리가 후킹할 Game2ViewController는 UIViewController의 sub이므로 Game2ViewController : UIViewController부분이 있고, class-dump의 결과에서 확인했던 것과 같이 - (int)recognizeAnswer 메서드 형태로 정의해준다.


%hook Game2ViewController 후킹할 클래스/인터페이스를 지정하고 %end로 후킹코드의 끝을 내준다.

%orig는 원래 메서드의 리턴값을 가져올 수 있으며, argument가 존재하는 경우에는 %orig(arg1, arg2, ...)형식으로 호출 가능하다.


여기서는 본래의 리턴값을 가져와서 정답/오답여부를 로그로 표출하고, 무조건 정답처리 할 수 있도록 코딩하였다.



3. 후킹 결과



정상적으로 후킹 되어 로깅이 되고있는 모습이다.



원래 정답은 비파형 / 세형이지만 아무 값이나 오답을 입력한 후 테스트 하였고 정답처리가 됨을 확인하였다. :)



4. 후기

Logos를 이용해서 트윅제작, 시스템콜 후킹, UI버튼을 추가하는 등의 다양한 여러작업이 가능하다고 들었는데, 천천히 하나씩 해보고 싶다. 우선 다음 게시글은 Frida를 이용해서 똑같은 행위를 하는 후킹코드를 작성 해볼 예정이다.





'Study > iOS' 카테고리의 다른 글

iOS Hooking#3(Cycript)  (2) 2017.05.06
iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13
  1. 1357 2017.04.18 15:18

    유익한 정보네요 ~

1. ptrace 

 ptrace는 여러 유닉스와 유닉스 계열 운영 체제에서 발견되는 시스템 호출이다. ptrace(이 이름은 "process trace"의 축약형이다)를 통해 컨트롤러가 대상의 내부 상태를 조사하고 조작하게 함으로써, 한 프로세스가 다른 프로세스를 제어할 수 있다. ptrace는 디버거와 다른 코드 분석, 특히 소프트웨어 개발을 도와주는 도구들에서 사용된다.

[출처] 위키백과 https://ko.wikipedia.org/wiki/Ptrace


2. 목적

 본래의 목적은 안드로이드 LD_PRELOAD 환경 변수에 라이브러리명을 인젝션하여 특정 함수를 후킹하는 것

2-1) LD_PRELOAD 

 프로세스 실행 전 라이브러리 로딩 과정에서 해당 환경변수에 추가되어있는 라이브러리를 우선적으로 로딩하게 만드는 환경변수

2-2) 메인아이디어 

 zygote를 kill하게되면 init프로세스에서 zygote프로세스를 재생성하며, 이때 LD_PRELOAD환경 변수가 재로드되고, 이때 값을 조작하여 원하는 라이브러리를 인젝션한다.


2-3) 목적 달성 과정

 - zygote process kill (성공)

 - new zygote process의 pid를 알아냄 (성공)

 - new zygote process에서 실행하는 execve 실행 시점에서 환경변수 값 조작 (실패)

 - 후킹 여부 확인 (실패)


3. aarch32 vs aarch64

3-1) register갯수가 다름

 - register갯수가 대폭 추가되면서 ptrace를 이용하여 system call을 이해하기 위해 사용되는 register번호 또한 달라졌으며, 그에 따라 코드에서 사용하는 구조체 및 상수 또한 달라졌다.

 

aarch32 

aarch64 

system call number표시 reg 

 8

 IP reg

ARM_ip 

 12

 return 값 reg

reg표시 structure

pt_regs

 user_regs_struct

 syscall number 참조

 리눅스 syscall number와 동일

ndk-bundle\include\asm-generic\unistd.h


 - aarch64 레지스터 설명

r30 (LR): The Link Register, is used as the subroutine link register (LR) and stores the return address when Branch with Link operations are performed.

r29 (FP): The Frame Pointer

r19…r28: Callee-saved registers

r18: The Platform Register, if needed; otherwise a temporary register.

r17 (IP1): The second intra-procedure-call temporary register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r16 (IP0): The first intra-procedure-call scratch register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r9…r15: Temporary registers

r8: Indirect result location register

r0…r7: Parameter/result registers


3-2) zygote 프로세스

 - aarch32에서는 zygote process밖에 존재 하지 않지만, aarch64에서는 zygote / zygote64 process가 존재함 

 - aarch64에서 zygote 프로세스는 두 개지만 zygote process를 kill하면 zygote64 또한 재 실행되는 것으로 보임


3-3) zygote 프로세스 kill 후

 - 새 프로세스 생성 전 aarch32에서는 stat64 system call을 사용하여 실행 바이너리(app_process)의 정보를 얻어 오지만, aarch64에서는 fstat64 system call을 사용하여 실행 바이너리(app_process32 / app_process64)의 정보를 얻어오며, aarch64의 app_process 바이너리는 app_process64의 심볼릭 링크이다.

 - aarch32에서는 fork system call로 zygote process를 생성하지만 aarch64에서는 clone system call을 이용

 - 개인적으로 fork / clone system call의 가장 큰 차이점은 프로세스 복사(생성) 이 후 실행 할 바이너리를 경로로 넘겨주느냐(fork), file descriptor(clone)의 차이가 가장 큰 외적 차이로 보이며, 조사를 통한 내적 차이는 parent process로부터 메모리 스택을 공유(clone) / 비공유(fork)라고 생각하고, 전반적으로 clone의 system call이 fork에 비해 비교적 비용이 낮은 것으로 설명이 되어있었다.


3-4) execve system call 탐지 가능 여부

이 부분은 aarch64에서도 아마도(?) 탐지가 가능할 것이나 내 능력/자료가 부족한 것으로 생각된다.

 - ptrace를 사용하여 레지스터 값들을 조사할 때 실행되는 system call number에 execve system call에 해당하는 220번이 탐지 되지 않음

 - strace를 이용하여 값을 조사했을 때는 execve system call이 실행되는 것이 확인됨


4. aarch64에서 ptrace사용

4-1) 레지스터 값을 가져오기 위한 구조체 선언

1
2
3
4
5
6
7
8
9
10
//#include <linux/PTRACE.h>
//#include <sys/PTRACE.h>
 
//aarch64에서 달라진 구조체
struct user_regs_struct gregs;
 
//aarch64에서 추가로 필요한 부분
struct iovec iovecs;
iovecs.iov_base = &gregs;
iovecs.iov_len  = sizeof(gregs);


4-2) ptrace attatch

1
2
3
4
5
6
7
8
// step 1. init프로세스에 PTRACE attatch 
ret = PTRACE(PTRACE_ATTACH, INIT_PID, (void *)10);
if(ERROR == ret){
    printf("[ERROR] PTRACE_ATTACH failed return = %d\n", ret);        
}
else{
    printf("[INFO] PTRACE_ATTACH SUCCESS\n");    
}



4-3) 레지스터 값 얻어오기

1
2
3
4
5
6
7
8
9
10
11
// cpu 레지스터 획득
ret = PTRACE(PTRACE_GETREGSET, INIT_PID, (void *)NT_PRSTATUS, &iovecs);
        
if ( ret < 0 ) {
    printf("[ERROR] PTRACE_GETREGSET ERROR : %d\n", ret);
            
}       
else{
    printf("[INFO] PTRACE_GETREGSET SUCCESS : %d\n", ret);                
}








4-4) system call 탐지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define SYSCALL_REGISTER(r)     r.regs[8]
#define RESULT_REGISTER(r)      r.regs[0]
#define IP_REGISTER(r)          r.regs[12]
 
//gregs.regs[28]이 0이 무엇을 뜻하는 지는 알수 없으나,
//여러 번 반복해보는 과정을 통해 0인 경우일 때가 new pid가 리턴되는 것을 확인함
//그 중에서도 리턴 값이 정상인 것과 비정상인 값이 있었는데,
//정상 값을 찾기 위해 maximum pid값을 지정함(cat /proc/sys/kernel/pid_max -> 32768)
if ( 220 == SYSCALL_REGISTER(gregs) && 
      40 == IP_REGISTER(gregs)        && 
       0 == gregs.regs[28]             &&
   32768 >= RESULT_REGISTER(gregs) ) {
            printf("[*] Found a new zygote64 process: %ld\n", RESULT_REGISTER(gregs));
            
            new_zygote_pid = RESULT_REGISTER(gregs);
}



4-5) ptrace detach

1
2
3
4
5
6
7
//ptarce detach
if(PTRACE(PTRACE_DETACH, INIT_PID, (void*)10< 0) {
        printf("[ERROR] PTRACE_DETACH error\n");
}
else{
        printf("[INFO] PTRACE_DETACH SUCCESS\n");
}



1. JDB

The Java Debugger, jdb, is a simple command-line debugger for Java classes. It is a demonstration of the Java Platform Debugger Architecture that provides inspection and debugging of a local or remote Java Virtual Machine.

[출처] http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html

간단히 정리하면 로컬 혹은 원격 자바 가상머신에서 실행되는 자바 프로그램을 디버깅할 수 있는 디버거입니다.

이 JDB를 이용하여 앱을 디버깅 할 수 있습니다.

위치 : ..../JDK_HOME/bin/jdb


2. 안드로이드 앱에 JDB Attach방법

2.1 디버깅 앱 포트번호 알아내기

먼저, 안드로이드 앱에서 사용하고 있는 포트번호를 알아내야합니다. 

앱 실행 전 adb jdwp 실행 결과와 디버깅할 앱 실행 후 adb jdwp 결과를 비교하여 앱에서 실행하는 포트를 알아냅니다.

아래는 결과물을 diff하여 알아낸 창입니다.

 보통은 맨 아래에 추가가 되는 형태인 듯하네요. 예시에서 디버깅 할 앱에서 사용하고있는 포트 번호는 8160입니다.


2.2 디버깅 포트를 로컬 포트로 포워딩

adb 명령어를 통해 디버깅 포트를 로컬 포트로 포워딩해줍니다.

adb forward tcp:[로컬포트] jdwp:[디버깅포트]


2.3 jdb실행

로컬포트를 이용하여 jdb를 attach합니다.

위의 예시는 디버깅포트가 8218을 사용하고 있고, 로컬포트 11111로 포워딩 해주고 난 후

jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=11111(포워딩한 로컬포트) 를 실행하여

JDB를 Attach하였습니다.


3. 기본 JDB 명령어

명령어 

사용법 

설명 

next

next 

다음 라인 실행 

local

local 

현재 로컬 영역 변수 보기 

threads 

threads 

현재 실행 중인 스레드 확인 

methods 

methods [Class Name] 

해당 클래스가 사용하는 

메서드 출력 

stop in

stop in [method name] 

해당 메서드에 

브레이크포인트 설정 

print

print [value] 

특정 변수 값 출력 

eval 

eval [Java Code] 

자바코드를 실행시킴 

 run

run 

현재 브레이크 포인트
무시하고 실행 

[참조] 안드로이드 모바일 앱 모의 해킹(지은이. 조정원, 김명근, 조승현, 류진영, 김광수)




Drozer 기본 명령어 정리 

Drozer_command.xlsx



File Download 취약점

외부 입력 값에 대해 경로 조작에 사용될 수 있는 문자를 필터링 하지 않으면, 예상 밖의 접근 제한 영역에 대한 경로 문자열 구성이 가능해져 시스템 정보누출, 서비스 장애 등을 유발 시킬 수 있는 취약점

출처: http://skynarciss.tistory.com/37 [IT 보물창고]


1. 대상 확인

작성된 게시글의 첨부파일을 다운받는 페이지를 살펴봅시다.

업로드된 파일명을 클릭하면 다운로드를 받을 수 있습니다.

Burp Suite를 이용하여 파일 다운로드 요청이 어떻게 전송되는지 살펴보겠습니다.


파일 다운로드를 하기 위해 fileDown.jsp로 요청을 보내는데, 파일명을 GET방식으로 fileName이라는 파라미터에 값을 셋팅하여 전송하고있습니다.


GET방식이기 때문에, 같은 url을 입력하면 파일을 다운 받을 수 있습니다.

 


2. 로직

이 게시판에서 파일을 다운받게 해주는 로직을 추측해보고, 실제 소스코드를 봅시다.

1. GET 파라미터인 fileName에 파일명이 전달된다. 

2. 파일 업로드 시 파일을 저장한 저장 디렉토리의 위치에 파일명을 덧붙혀 파일경로를 완성한다.

3. 해당 파일 경로에 있는 파일을 클라이언트로 전송한다.


실제 소스코드를 보면 위에서 추측한 로직과 동일한 것을 알 수 있습니다.

이 게시판은 C드라이브 아래에 있는 save_dir 디렉토리에 파일을 저장하고 있었네요.


3. 공격

File Download 취약점이 존재하는 지 알아 보기 위해 디렉터리 이동 명령어들이 실행 되는지를 아래와 같이 확인해 볼 수 있습니다.

파라미터는 test/../148609......jpg 의 형태로 입력이 되었습니다.

이 파일 명이 의미하는 바는 잘 아시다시피 현재 경로에서 test디렉터리 아래 -> 한 단계 상위 -> 에 있는 148609......jpg가 됩니다. test디렉터리 아래 -> 한 단계 상위는 서로 상쇄되어 결국 148609......jpg가 되는 것인데,

이 테스트의 의미는 디렉터리 이동 명령어들이 필터에 걸리지 않는 지, 혹은 다른 조치가 되어 있는 지를 확인하는데 의미가 있습니다.

위 테스트를 하였을 때 성공적으로 튤립 그림을 다운받을 수 있었으므로, File Download취약점이 있다고 보여집니다.


파일 이동 명령어가 실행이 되는 상태라면 자유자재로 서버의 파일들을 다운 받을 수 있습니다.(물론 원하는 파일의 위치를 알아야겠지만요)

이 게시물에서는 예시로 C:/Window/System32 경로에 test.txt파일을 만들어서 공격 예시를 들어보도록 하겠습니다.


구조는 어렵지 않고 다만 파라미터에 원하는 경로를 조립하여 넘겨주었습니다.

결과적으로 해당 경로에있는 test.txt파일을 다운 받을 수 있었습니다.



4. 조치 방법

각 개발 프레임 워크 별 설정방법이 있는 것 같습니다만, 우리 게시판 프로젝트는 순수 jsp로 짜여진 게시판이므로ㅠㅠ 간단한 필터링을 통해 조치를 해보도록 하겠습니다.

위의 소스코드와 같이 입력받은 파라미터를 곧바로 사용하지 않고 디렉터리 이동에 사용되는 문자("\.", "..", "/"들을 공백으로 치환해주었습니다.

그리고 파일이 존재 하지 않는 경우에는 (더 좋은 처리가 있겠지만..) 더미 파일을 전송하도록 하여 처리를 했습니다.






'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

SQL

RDBMS(Relation DataBase Management System)의 데이터를 관리하기 위해 설계 된 특수 목적 프로그래밍 언어로써 쉽게 말해 데이터베이스를 핸들링 하기 위해 사용하는 언어입니다.


SQL Injection

응용 프로그램 보안 상의 허점을 의도적으로 이용해, 개발자가 생각지 못한 SQL문을 실행되게 함으로써 데이터베이스를 비정상적으로 조작하는 코드 인젝션 공격 방법입니다.

[출처] 위키백과(https://ko.wikipedia.org/wiki/SQL_%EC%82%BD%EC%9E%85)


Blind SQL Injection

SQL Injection의 결과를 이용하여 참, 거짓 값을 구별 할 수 있을 때 사용할 수 있는 방법으로 장님이 길을 지팡이로 짚으며 가는 듯한 과정이 있어 이를 Blind SQL Injection이라고 합니다.


1. 대상 확인

언제나 처럼 게시판을 이용하여 정리해보도록 하겠습니다.

먼저 게시판의 검색 부분을 살펴보겠습니다.

검색조건을 선택하고 검색 키워드를 입력한 후 검색 버튼을 누르면 해당하는 내용을 검색 할 수 있습니다.

검색 조건은 작성자 / 키워드 / 작성자 + 키워드이며 공란인 경우 아래와 같은 알럿메시지를 표출합니다.

검색에 이용되는 키워드는 쿼리 작성에 사용이 될텐데 검색 후 URL에 파라미터가 표시되지 않는 것으로 보아 POST방식으로 파라미터가 전달 되고 있는 것 같습니다.

검색 할 당시의 패킷을 Burp Suite로 확인 해보도록 하겠습니다.


위에서 추측한대로 검색에 관련된 파라미터들(검색 조건/검색 키워드)이 POST방식으로 전달되고 있는 것을 확인 할 수 있습니다. (searching_condition=title&searching_keyword=TE)


2. 우회 과정

이 키워드에 전달되는 파라미터에 쿼리를 인젝션하여 특정 게시물의 비밀번호를 알아내보겠습니다.

결론부터 이야기하자면 인젝션하여 서버에서 실행 될 쿼리의 예시는 아래와 같습니다.

SELECT letterNum, userID, letterTitle, secretYN
FROM board
WHERE userID LIKE '%USER%'

AND ((ascii(substr((SELECT letterPW from board where letterNum = 6), 1, 1))) = 48)#%'

현재 서버에서 실행되고 있는 쿼리는 

SELECT letterNum, userID, letterTitle, secretYN
FROM board
WHERE userID LIKE '%{INPUT}%'

의 형태이고, 이 INPUT안에 쿼리를 삽입하여 위처럼 공격 쿼리를 만들어 내야합니다.

(지금은 직접 개발한 게시판이기 때문에 DB정보와 쿼리문의 구조를 알고있지만,

실제로는 추측/유추 해야하는 과정이 필요합니다.)

따라서 INPUT에 삽입될 쿼리는 아래와 같습니다.

USER%'

AND ((ascii(substr((SELECT letterPW from board where letterNum = 6), 1, 1))) = 48)#%'

AND 뒷 부분이 핵심이므로 이 부분을 상세히 살펴보도록하겠습니다.

1) SELECT letterPW FROM board WHERE letterNum = 6

-> 이 예시에서는 게시글 번호 6번 글에 대한 패스워드를 유추하고 있습니다. 따라서 6번 게시물의 패스워드를 조회해 옵니다.


2) substr( 6번게시물 패스워드, 1, 1)

-> mysql에서 실행되는 함수로 문자열의 첫 번째 글자부터 한 글자를 조회해옵니다.

-> 결과적으로 6번 게시물 패스워드의 맨 첫 글자를 가져올 수 있습니다.


3) ascii(문자)

-> 해당 문자를 아스키 코드 값으로 변환해줍니다.


4) 결과

6번 게시물 패스워드의 첫 글자의 아스키코드가 아스키코드 값 48과 같은 지를 반환합니다.

위의 결과가 true인 경우, 원래의 쿼리와 결과가 종합되어 모든 게시물이 출력되고(true and true이므로),

false인 경우 게시물 조회 결과가 없음으로 표시됩니다.


패스워드를 한 글자씩 찾는 스크립트가 필요합니다. 

스크립트 풀 소스는 첨부 하고, 주요 부분만 확인하도록 하겠습니다.  : )

첨부 :  blinSqlInjection.py

실습환경이기때문에, 비밀번호는 숫자와 영대소문자로만 이루어져있다는 가정을 하였습니다.

스크립트는 파이썬으로 작성하였으며 스크립트 실행 전 설치해야할 파이썬 라이브러리로는

http리퀘스트를 위한 requests와 결과를 파싱하기 위한 Beautifulsoup가 있습니다. 


헤더 값은 Burp Suite에서 얻은 패킷의 값과 동일하게 맞추어주고, 파라미터를 변경합니다.

우리가 수정해서 삽입할 쿼리를 완성시킨 executeSql을 키워드 파리미터에 셋팅해줍니다.

requests를 이용하여 리퀘스트를 보내고 받은 결과를 BeautifulSoup를 사용하여 파싱합니다.

해당 게시판의 list.jsp 응답 내용 중 "총 게시물 : x개" 로 body에 응답이 오는데 이 부분을 파싱하여

0개인 경우에는 찾지 못한 것으로, 0개 이상인 경우에는 찾은 것으로 판별하였습니다.


아래는 파싱하여 게시물의 갯수를 int형으로 변환하여 리턴해주는 함수입니다. 

첫 째줄에서는 공백을 제거하였고, ":"로 스플릿한 뒷부분을 공백제거하여 이 후

첫 글자를 int형으로 변환한 후 리턴해줍니다.


비교할 검사 문자는 0~9, a ~ z, A ~ Z 입니다. 각 문자들이 아스키 코드표 상 연속되어 존재하지 않기 때문에

구간 구간 점프를 뛰어야하는 부분을 if문으로 걸러주었으며, 찾는 문자 구간에 문자가 없으면 패스워드의 끝으로 인식하고 있습니다. ( elif int(asciiNum) > 122 부분)

문자를 찾은 경우에는 targetPW라는 필드에 찾은 문자를 덧 붙혀주고, 검사 문자 순서를 다음으로 넘긴 후

비교 문자를 0('48')로 초기화 시켜주고, 쿼리문을 갱신합니다.


위 과정을 모두 마친 후 추출해낸 패스워드를 확인합니다.


스크립트 실행 결과


3. 조치 방법

1. jspBoardProject#2(XSS) 에서 조치했던 것 처럼 입력 값에 필터링을 수행하여 검사할 수 있습니다.

2. JAVA/JSP에서는 쿼리 실행을 Statement클래스를 사용하지 않고 PreparedStatement를 사용하는 방법이 있습니다.


필터링에 관해서는 앞서 다뤄본 방식과 별반 다르지 않기 때문에, PreparedStatement를 사용하는 방법으로 조치해보도록 하겠습니다.

먼저, 문제가 되는 부분의 소스코드를 살펴보면,

아래와 같이 변수가 쿼리에 직접적으로 연결되는 구조로 Injection하기 너무나 편리하게 되어있는 것을 확인 할 수 있습니다.



이 부분을 PreparedStatement 인터페이스를 사용하여 다음과 같이 조치 할 수 있습니다.

바뀐 점은 일반 Statement인터페이스에서는 쿼리와 Input value가 직접적으로 연결되어 실행이 되지만,

PreparedStatement에서는 쿼리문에서는 ? 로 인자 표시를 하고 후에 setString과 같은 메서드를 사용하여

변수를 추가 해주고있습니다.

코드 수정 후 스크립트 결과를 보면 아래와 같이 조치가 된 것을 확인 해볼 수 있습니다.



여담.

order by 뒷 부분 역시 PreparedStatement방식으로 사용 해보려고했으나, setString(4, sorting_cond); 와 같은 방식으로 추가했을 때 sorting_cond가 상수 값으로 인식되어 정상적으로 정렬이 되지 않았습니다.

외부에서 직접적으로 입력을 받는 부분이 아니기때문에 문제가 없을 것으로 생각되어 기존처럼 쿼리에 연결을 하였습니다.

'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

파라미터 변조

- 클라이언트와 서버 사이에서 주고받는 파라미터 값을 조작

- 매개변수라고 하며 웹페이지상에서 request 대한 값을 가지고 있는 변수

- 사용자 기밀정보, 허가, 상품의 개수, 가격 등을 조작

- 이러한 정보는 쿠키나 숨겨진  필드, UR 쿼리 문자열 등에 저장


출처: http://elmosec.tistory.com/4 [보알못 엘모의 보안 겅부 필기 공책 :)]


게시판으로 예시를 들어보도록 하겠습니다.

게시판의 메인 페이지입니다. 여러 글들이 있고, 그 중 비밀글인 글들이 보입니다.

파라미터 변조를 통해 비밀글에 접근을 해보도록 하겠습니다.


1. 대상 확인

1.1) 비밀글을 열람하려고 글을 클릭하면 비밀번호를 묻는 페이지가 나오고, 비밀번호가 틀리면 비밀번호가 틀렸다는 얼럿이 나타납니다. 


1.2) 서버에 날아가는 요청을 프록시 툴인 Burp Suite로 분석을 해보겠습니다.


우선, 요청 url은 openSecret_ok.jsp이며,

POST방식으로 입력한 패스워드가 평문으로 전송되고, 비밀 게시글의 번호가 idx라는 GET파라미터로 전송되고 있습니다.

이 페이지(openSecret_ok.jsp)에서 글의 인덱스와 비밀번호를 파라미터로 받아서 패스워드 검사를 하였는지, 비밀번호가 틀렸다는 알럿 메시지가 뜨는 자바스크립트를 응답으로 받았습니다.


2. 로직 분석

메인 아이디어는 아래와 같습니다. 이 게시판 서비스는 openSecret.jsp에서 비밀번호를 입력 받은 후 openSecret_ok.jsp에서 인증을 하여 인증에 성공한 경우 view.jsp에서 서비스가 실행 되는 구조로써 인증과 서비스 로직이 분리되어 있어 이 점을 이용하였습니다.


3. 우회 시나리오

1) openSecret_ok.jsp 에서 인증가능한 파라미터를 전달합니다.

 - 하나의 새 글을 작성하여 패스워드를 알고있는 글을 만들었습니다.

2) openSecret_ok.jsp의 인증을 따낸 후 view에 글을 열람할 인덱스를 전달 할 것이므로, 이 인덱스를 우리가 원하는 비밀글의 인덱스로 변경하여 전달합니다.


사실 비밀글이 아닌 곳에서 view.jsp에 전달되는 get 파라미터만 변경하여도 비밀글에 접근이 가능했었습니다..ㅋㅋ


조치 방안에서는 이 두 가지 문제점 모두 수정하였습니다. :)


4. 우회 과정

4.1 시나리오에서 언급했던 것 처럼 새 글을 비밀번호 1234로 하여 작성하였습니다. 

4.2 비밀번호 입력 후 패킷을 변조해봅시다.

패스워드를 1234로 변경하였고(입력할 때 부터 1234로 입력해도됩니다.), 인덱스가 원래 4였지만 18(우리가 게시한 글의 인덱스)로 수정하였습니다.

 

4.3 인증에 성공하자 예상한 대로 view.jsp에 GET방식으로 파라미터를 인덱스로 던지고 있습니다. 이 부분을 4(비밀게시글 인덱스)로 수정해보겠습니다.


4.4 우회 성공

5. 조치 과정

5.1 조치 전 소스 코드

openSecret_ok.jsp



view.jsp


위 로직의 가장 큰 문제점(본인이 짰지만..)이라고 생각한 건 위에서 말한 것 처럼 인증 로직과 서비스로직이 분리되어 있기 때문에 이 같은 취약점이 발생했다고 생각했습니다.

openSecret_ok.jsp에서는 인증만을 수행하고 view.jsp에서는 글을 보여주는 서비스 로직만 있으므로, 생각해낸 방법은 openSecret_ok페이지를 삭제하여 인증 과정을 view.jsp페이지에 넣는 방법을 사용해보기로 하였습니다.

5.1 조치 후 소스 코드

먼저, openSecret.jsp의 소스에서 입력 내용을 openSeceret_ok.jsp로 전송하던 부분을 view.jsp로 전송하도록 수정하였습니다.


이후 view.jsp에서는 pw를 추가로 파라미터로 수신하며, 비밀글 여부 체크 후 비밀번호 인증하는 로직을 추가하여 조치하였습니다.


'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

XSS

XSS(Cross-site Scripting)는 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말합니다.

스크립트가 삽입 가능할 때 쿠키 탈취, 악성코드 유포, 특정 사이트로 납치 등 많은 공격이 가능 합니다.


게시판으로 예시를 보도록 하겠습니다.

게시판에 글을 작성 하며 글 내용에 알림창을 띄우는 간단한 자바스크립트를 삽입하였습니다.



해당 글을 열람하기위해 제목을 누르는 순간 삽입한 javascript 코드가 실행 되는 것을 확인 할 수 있습니다.


게시판의 글 작성 로직을 살펴보면

1. write.jsp에서 작성자 / 제목 / pw 등의 입력을 받는다.

2. write.jsp에서 입력 값의 유효성을 검사한다.

3. wirte_ok.jsp에서 게시글을 DB에 등록한다.

위 과정을 통하여 글이 작성되는 것을 살펴 볼수 있습니다. 


아래의 자바스크립트 코드는 위 과정 중 2번에 해당하는 부분으로 클라이언트가 글을 작성 한 후 그 컨텐츠의 유효성을 체크하는 코드입니다. 

보이는 것처럼 글을 작성할 당시 각 필드의 값이 빈 값인지 여부만을 확인 하고 있습니다. 


이 코드만 보면.. 글 제목과 작성자 란에도 스크립트 삽입이 가능하며, 메인페이지만 열어도 스크립트가 실행되어 방문자 모두가 스크립트를 실행하게 될 수 도있 습니다.

이제 이 유효성 검사 부분을 수정하여 XSS를 방어하는 방법 중 입력 내용을 필터링 하도록 수정해보도록 하겠습니다.


각 필드의 공백 유효성 검사 한 후 공백이 아닌 경우 스크립트에 포함되는 문자열들을 빈문자열 혹은 다른 문자열로 대체하도록 필터링을 하는 코드를 추가하였습니다.


결과로 같은 스크립트를 삽입하였을 때 아래와 같이 게시글이 필터링되어 게시됩니다.


   



'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

+ Recent posts