짱짱해커가 되고 싶은 나

[안드로이드 취약점 진단] 3-9. 안전하지 않은 웹 뷰 취약점 본문

모바일

[안드로이드 취약점 진단] 3-9. 안전하지 않은 웹 뷰 취약점

동로시 2022. 6. 22. 19:45

Web View

웹 뷰는 안드로이드에서 웹 브라우저에서 보이는 화면을 표시하거나 웹 앱 혹은 하이브리드 앱 개발에 사용한다.

(하이브리드 앱은 네이티브 앱보다 개발 과정이 쉽고 기기 간 호환성을 해결하기 쉽다는 장점이 있다.)

  • 안드로이드 내부 모듈인 웹킷 랜더링 엔진 사용 (js 지원)
  • AndroidMainfest.xml) <uses-permission android:name="android.permission.INTERNET"/>
  • WebView 객체를 산언해서 사용

 

인시큐어뱅크 - Insecure Webview implementation

AndroidMainfest.xml

현재 apk에 INTERNET 사용 권한이 설정되어 있는 것을 확인할 수 있다.

 

MyWebViewClient

WebView를 사용하는 곳을 찾아보니 다음과 같이 WebViewClient를 상속받는 MyWebViewClient 클래스를 선언하고 ViewStatement에서 해당 객체를 WebView로 사용한다.

 

MyWebViewClient 코드를 보면 다음과 같다. 심플하게 webview랑 url을 받아서 loadUrl 함수를 통해 실행한다.

 

ViewStatement

먼저 uname을 intent를 통해 받아오고, getExternalStorageDirectory()를 통해 외부 저장소의주소를 가져온다. 대부분 별도로 추가한 거 없으면 내장 sd카드 경로를 알려준다.(참고로 API 29 이상부터는 보안상의 이유로 사용되지 않는 것 같다.)

그러니까 저장소/Statements_[사용자이름].html 이라는 파일을 만들고 파일 내용을 java 문자열로 변환해서 출력해준다.

만약에 해당 파일이 존재한다면, webView1 이라는 id를 가진 웹 뷰를 가져오고 file:// 을 이용해 아까 본 파일을 로드시킨다!

-> 아니 굉장히 위험해보이는???

쨌든, 설정을 보면 JavaScript 도 ok. 폼에 입력된 데이터 저장

-> 오호 스크립트가,,

 

 

그렇다면 저 파일은 언제 생성되는가 -> DoTransfer

 

DoTransfer

코드를 보면, transfer에 성공했을 때랑 실패했을 때 모두 /Statements_[이름].html 파일을 생성해서 저장한다.

public void run() {
                    this.this$1.AsyncHttpTransferPost("result");
                    if (this.this$1.this$0.result == null) {
                        return;
                    }
                    if (this.this$1.this$0.result.indexOf("Success") != -1) {
                        Toast.makeText(this.this$1.this$0.getApplicationContext(), "Transfer Successful", 1).show();
                        try {
                            this.this$1.this$0.jsonObject = new JSONObject(this.this$1.this$0.result);
                            this.this$1.this$0.acc1 = this.this$1.this$0.jsonObject.getString("from");
                            this.this$1.this$0.acc2 = this.this$1.this$0.jsonObject.getString("to");
                            System.out.println("Message:" + this.this$1.this$0.jsonObject.getString("message") + " From:" + this.this$1.this$0.from.getText().toString() + " To:" + this.this$1.this$0.to.getText().toString() + " Amount:" + this.this$1.this$0.amount.getText().toString());
                            String str = new String("\nMessage:Success From:" + this.this$1.this$0.from.getText().toString() + " To:" + this.this$1.this$0.to.getText().toString() + " Amount:" + this.this$1.this$0.amount.getText().toString() + "\n");
                            try {
                                BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory() + "/Statements_" + this.this$1.this$0.usernameBase64ByteString + ".html", true));
                                bufferedWriter.write(str);
                                bufferedWriter.write("<hr>");
                                bufferedWriter.close();
                            } catch (IOException e) {
                                e.toString();
                            }
                        } catch (JSONException e2) {
                            e2.printStackTrace();
                        }
                    } else {
                        Toast.makeText(this.this$1.this$0.getApplicationContext(), "Transfer Failed", 1).show();
                        System.out.println("Message:Failure From:" + this.this$1.this$0.from.getText().toString() + " To:" + this.this$1.this$0.to.getText().toString() + " Amount:" + this.this$1.this$0.amount.getText().toString());
                        String str2 = new String("\nMessage:Failure From:" + this.this$1.this$0.from.getText().toString() + " To:" + this.this$1.this$0.to.getText().toString() + " Amount:" + this.this$1.this$0.amount.getText().toString() + "\n");
                        try {
                            BufferedWriter bufferedWriter2 = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory() + "/Statements_" + this.this$1.this$0.usernameBase64ByteString + ".html", true));
                            bufferedWriter2.write(str2);
                            bufferedWriter2.write("<hr>");
                            bufferedWriter2.close();
                        } catch (IOException e3) {
                            e3.toString();
                        }
                    }
                }

그러면 폼에다가 스크립트를 삽입하면 저 파일에는 <script>가 들어가고 viewstatement를 했을 때 읽어오는 과정에서 해당 스크립트가 실행될 것이다.

wow- xss 된걸 확인 가능

 

 

대응 방안

  • 웹 뷰 취약점은 구글에서 안드로이드 4.3 이하 환경에서의 보안 패치 중단
    -> 따라서 웹 뷰를 구현하기 위해서는 4.4 이상 버전 사용
  • 웹 뷰에서 JavaScript를 사용할 때는 addJavascriptInterface -> android.webkit.JavascriptInterface로 호출해서 사용하도록 변경

1. 웹 뷰의 주소가 올바른 URL인지 검증 로직 구현

2. 웹 뷰 대신 오픈소스 HTML5 런타임 프레임워크인 Crosswalk 사용
(CrossWalk - 안드로이드가 제공하는 native API를 사용하지 않고 HTML5 앱 개발, 배포 가능, 플랫폼 버전에 종속적X, 모든 디바이스에서 돌아가는 단일 런타임 구현 가능)

3. 사용자가 입력 가능한 모든 구간에 허용되는 문자 외에 모두 null로 치환 (ex.계좌번호는 숫자만 가능하게 형식 검증)

4. JS가 필요하지 않는 경우에는 setJavaScriptEnabled 메소드 false로 설정


ps. 메타스플로잇

 

 

Comments