우선 안드로이드 APK 파일은 JAR 와 호환되는 형태로 되어있다. 즉, 코드를 빌드를 하여 나오는 APK  파일은 zip 포맷과 유사한 jar 와도 비슷하다. 특히 서명에 대한 방식은 JAR 와 APK 와 매우 비슷하다. 실제로 서명을 사용해 빌드된 APK 파일 확장명을  zip 으로 변경하고 압축을 풀어보면 META-INF 디렉토리 내부에 이와 관련한 파일이 생성된 것을 볼 수 있다. 

  이 파일은 MANIFEST.MF, CERT.SF, CERT.RSA 파일이다. 


  MANIFEST.MF 파일은 텍스트로 되어있으며 META-INF 디렉토리 내부의 파일을 제외한 APK 패키지 내부 모든 파일(Entry)에 대한 리스트를 보여준다. 각 리스트의 항목에는 파일 경로와 각 파일의 SHA1 해쉬 값(파일을 읽어서 만든다.)을 Base64 로 인코딩 하여 나타내고 있다. 



  CERT.SF 도 마찬가지로 텍스트로 되어있으며, MANIFEST.MF 파일과 구조가 비슷하다. 다만  MANIFEST.MF 에 등록된 각 파일에 대한 해쉬 값은 개인키(APK 빌드할 때 사용되는 인증서, 패스워드와 관련되어 있다고 생각하면 된다) 를 이용하여 SHA1 RSA 로 암호화 한 뒤에 Base64 로 인코딩 되어 있다. 또,  MANIFEST.MF 파일 자체의 위변조를 막기 위하여 MANIFEST.MF 파일의 값도 포함되어 있다. 개인키로 암호화된 값들은 공개키로 복호화할 수 있다. 




 CERT.RSA 는 공개키 인증서이며 소유자, 발행자, 일련번호, 유효기간, 인증서 지문,  알고리즘등이 표시되어 있다.  keeytool 을 이용하여 값을 확인할 수 있다.






   안드로이드 APK 패키지 파일의 서명은 기존 JAVA 에서 제공하는 JAR 파일의 서명과 동일한 방식을 사용한다. 마찬가지로 APK 파일 설치시 JAVA 에서 기본적으로 제공하는 jar 서명 검증 관련 API 를 이용하여 APK 의 무결성을 확인한다. 실제 예를 들자면 Android 내부의  PackageParser 클래스에서도 JarFile 클래스를 이용하여 APK 파일을 열고 내부의 Entry(파일) 리스트를 받아와서 InputStream  을 통하여 파일을 읽는다. 이 과정에서 내부적으로 JarVerifier 클래스가 서명 정보를 확인한다. 서명 정보를 확인할 때, CERT.SF 내부의 암호화된 값들을 공개키를 이용하여 복호화 하고 MANIFEST.MF 의 파일 해쉬 값들과 비교한다. 또, 실제 파일의 SHA1 값들과 MANIFEST.MF 내부의 값들이 일치하는지 검사한다.(순서는 확인하지 못 하였다.) 

   

  이런 과정 덕분에 안드로이드 앱은 빌드시에 사용되는 인증서가 있지 않는한 위변조된 APK 파일은 원본과 구분될 수 있다. 즉, 원본 앱을 업데이트 하는 방식으로 위변조 APK 를 설치할 수 없다. (물론 인증서를 탈취하면 이야기가 달라진다.) 그렇다고 해서 악의적으로 위변조된 APK 파일에 대한 위협이 사라지는 것은 아니다. 만약 원본과 거의 비슷한 모양새로 만들어 빌드하고 다른 인증서로 배포하면 사용자 입장에서는 속아 넘어가서 설치하고 스미싱을 당하거나, 서버와 통신하는 클라이언트 앱의 경우 서버 공격에 대한 빌미를 줄 수 있다. 


  앱이 위변조 되었는지 확인하기 위하여 불가피하게 서버와의 통신이 필요하다. (이 내용은 지금 포스팅과 시리즈로 엮어서 다음 포스팅에서 다루겠다.) 서버로 APK 파일에 대한 고유 키 값을 보내줘야할 때, 인증서 정보(인증서 지문) 만큼 확실한 값은 없다. 


  우선 APK 파일을 압축을 풀면 나오는 META_INF/CERT.RSA  파일에 대한 인증 정보를 확인하는 방법은 다음과 같다.


 keytool -printcert -file  CERT.RSA 

  

  이 명령어를 실행하면 인증서 정보(인증서 지문, Certificate Fingerprint)를 얻을 수 있다.

   

안드로이드 코드상에서 얻는 방법은 다음과 같다. 

Context context = getApplicationContext();
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
String cert = null;
try {
    PackageInfo packageInfo = packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
    Signature certSignature =  packageInfo.signatures[0];
    MessageDigest msgDigest = MessageDigest.getInstance("SHA1");
    msgDigest.update(certSignature.toByteArray());
    cert = Base64.encodeToString(msgDigest.digest(), Base64.DEFAULT);
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}

  

     

  위 코드에서 cert  변수에 이 앱을 빌드할 때 사용된 인증서의 정보 값이 들어간다. 이 값과 APK 파일의 CERT.RSA 파일로부터 'keytool -printcert -file CERT.RSA' 으로 얻은 값을 Base64 로 인코딩 하여 비교하면 동일함을 알 수 있다.  물론 해쉬 알고리즘을 SHA-256 또는 MD5 를 이용하여 요약해도 마찬가지다.  


  하지만, 서버나 외부에서 APK 파일의 인증서 정보 값을 얻을 때는 안드로이드 내부에서 얻는 코드를 이용할 수 없다. 

  그래서 다음과 같은 클래스를 만들어 보았다.   APK 파일의 인증을 검증하는 과정도 포함되어 있으며 잘못된 인증 값을 갖고 있을 경우 예외를 발생시킨다.

  테스트 완료 하였으며, 아마도 안드로이드 내부에서 다른 APK 파일 인증 정보를 읽어오는 것도 가능할 것이다. 


  (코드가 다소 길다. 읽기 쉽도록 소스 파일도 함께 첨부한다.  )

 APKCertExtractor.java



APKCertExtractor:

import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.xml.bind.DatatypeConverter; import kr.re.dev.server.APKCertExtractor.APKCertExtractionException.ErrType; /** * APKCertExtractor * * - APK 파일내의 인증 정보를 SHA1, base64 로 만들어 반환한다. * - APK 파일의 유효성을 검사한다. * - 2개 이상의 서명을 사용하는 APK 파일에 대하여 사용할 수 없다. * 하지만, Play store 에서도 2개 이상의 서명을 갖고 있는 APK 를 허용하지 않는다. * * ::사용법 * - APKCertExtractor.execute(apk 파일 경로); * - 인증 검증 실패시 APKCertExtractionException 예외 발생. * * http://dev.re.kr */ public class APKCertExtractor { public static void main(String[] args) { String cert = ""; try { cert = APKCertExtractor.execute( "test.apk"); } catch (APKCertExtractionException e) { e.printStackTrace(); cert = e.getMessage(); } System.out.println(cert); } /** * 인증서 지문을 반환한다. * @param apkFilePath APK 파일의 경로. * @return * @throws APKCertExtractionException */ @SuppressWarnings("resource") public static String execute(String apkFilePath) throws APKCertExtractionException { try { // JarFile 클래스는 java.util.zip.ZipFile 를 상속받아 구현되었다. // jar 파일에 대한 설명은 다음 블로그 페이지에 자세히 소개되어 있다. // http//www.yunsobi.com/blog/62 // 이 클래스는 jar(zip) 의 엔트리(파일 정보) 및 파일을 읽기 위하여 사용된다. JarFile jarFile = new JarFile(apkFilePath); // APK 파일 내의 AndroidManifest.xml 의 엔트리와 인증 정보를 읽어온다. JarEntry manifestEntry = jarFile.getJarEntry("AndroidManifest.xml"); if(manifestEntry == null) throw APKCertExtractionException.newInstance(ErrType.WrongAPKFormat, apkFilePath, null); Certificate[] certs = loadCertificates(jarFile, manifestEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, manifestEntry.getName(), null); Certificate cert= certs[0]; // APK 파일 내의 모든 엔트리의 인증을 검증한다. verifCertificates(jarFile, cert); // 바이트 배열 타입의 인증 정보를 SHA1 Base64로 변환. String hash; try { hash = certToSHA1(cert); } catch (CertificateEncodingException e) { e.printStackTrace(); // 인증서에 문제 있을 때 발생. throw APKCertExtractionException.newInstance(ErrType.WrongCert, apkFilePath, e); } return hash; } // 파일 경로등에 문제 있을 때 발생한다. catch (IOException e) { throw APKCertExtractionException.newInstance(ErrType.ReadFail, apkFilePath, e); } } /** * APK 내부 파일의 인증을 확인한다. * @param jarFile * @param cert */ private static void verifCertificates(JarFile jarFile,Certificate cert) throws APKCertExtractionException { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = (JarEntry) entries.nextElement(); // 서명되지 않는 디렉토리인 META-INF 는 건너뛴다. if (jarEntry.isDirectory() || jarEntry.getName().startsWith("META-INF/") || jarEntry.getName().contains(".DS_Store")) { continue; } Certificate[] certs = null; certs = loadCertificates(jarFile, jarEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, jarEntry.getName(), null); Certificate localCert = certs[0]; // 인증 정보가 없는(서명이 되지 않은) 파일 발견. // APK 압축을 풀고 임의의 파일을 넣거나 제거하여 다시 APK 파일로 압축했을 때 발생한다. if (localCert == null) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } // 인증 정보가 서로 다른 엔트리 발견. // 물론 설치는 안 되겠지만, 위변조 시도되는 앱으로 의심된다. else if (!cert.equals(localCert)) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } } } /** * Certificates 객체를 반환한다. * @throws APKCertExtractionException */ private static java.security.cert.Certificate[] loadCertificates(JarFile jarFile, JarEntry jarEntry) throws APKCertExtractionException { if(jarEntry == null || jarFile == null) return null; try { // JarEntry 로부터 Certificate 객체를 얻기 위해서는 JarEntry 를 검증하기 위하여 끝까지 다 읽어야 한다. // 이 과정에서 내부적으로 JarVerifier 클래스를 통하여 인증에 대한 검증이 이뤄어진다. byte[] buffer = new byte[1024]; InputStream is = jarFile.getInputStream(jarEntry); try { while (is.read(buffer, 0, buffer.length) != -1) {} // 테스트 결과 MATA-INF 폴더의 파일 내에 해당 엔트리의 인증값들은 있지만, // 실제 파일이 존재하지 않을 경우 아래 예외가 발생한다. // 역시 위변조된 앱일 가능성이 크다. // 물론 이 경우도 일반적인 안드로이드 폰에서는 설치조차 되지 않는다. } catch(SecurityException e) { throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), e); } is.close(); buffer = null; return (java.security.cert.Certificate[])jarEntry.getCertificates(); } catch (IOException e) { System.err.println("Exception reading " + jarEntry.getName() + " in " + jarFile.getName() + " " + e); } return null; } /** * 서명 깂을 SHA1 해쉬로 변경하여 Base64 로 만든 String 값으로 반환한다. * @param cert * @return * @throws CertificateEncodingException */ private static String certToSHA1(Certificate cert) throws CertificateEncodingException { byte[] certWith = null; // X509 인증 정보를 ASN.1 DER 구조의 byte 배열로 반환한다. certWith = cert.getEncoded(); MessageDigest md = null; try { // 인증 정보를 SHA1 해쉬로 변경. md = MessageDigest.getInstance("SHA1"); md.update(certWith); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return DatatypeConverter.printBase64Binary(md.digest()); } /** * 인증서 추출 예외상황. * @author ice3x2 */ public static class APKCertExtractionException extends Exception { private static final long serialVersionUID = 6796836839897143903L; private ErrType mError; protected static APKCertExtractionException newInstance(ErrType errType, String path, Throwable throwable) { String message = ErrType.toMessage(errType) + " (" + path + ")"; APKCertExtractionException apkCertExtractionException = (throwable == null)?new APKCertExtractionException(message):new APKCertExtractionException(message, throwable); apkCertExtractionException.mError = errType; return apkCertExtractionException; } private APKCertExtractionException(String message, Throwable throwable) { super(message, throwable); } private APKCertExtractionException(String message) { super(message); } /** * 에러 타입을 반환한다. * @return */ public ErrType getError() { return mError; } public static enum ErrType { /** * 잘못된 APK 파일 포맷. */ WrongAPKFormat("Wrong APK file format."), /** * APK 파일을 읽을 수 없음. */ ReadFail("APK file read failed."), /** * 잘못된 인증서. 파일에 대한 인증 정보가 존재하지 않는다. */ WrongCert("Wrong certificate. Certificate verified failed."), /** * 위변조가 의심되는 APK. 인증 정보 검증에 문제가 있다. */ ForgeryAPK("Wrong certificate. This package is suspected with forgery apk."); private String message; private ErrType(String value) { message = value; } protected static String toMessage(ErrType errType) { return errType.message; } } } }



출처 : Dev.re.kr (http://dev.re.kr/70)

안드로이드 최신 버전의 SDK에서 pm명령어 모음입니다. adb shell로 접근하여 얻을 수 있는 명령어입니다. adb shell 접근 방법은 아래에서 설명하겠습니다.

명령어를 통해 앱을 설치하거나, 삭제하고, 사용자를 추가하고, 삭제 할 수 있는 명령어도 있습니다. 현재 기본값으로 셋팅 된 설치 경로를 가져오는 get-install-location 도 있습니다. 일반적으로 [0/auto]가 설정되어 있습니다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
usage: pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]
       pm list permission-groups
       pm list permissions [-g] [-f] [-d] [-u] [GROUP]
       pm list instrumentation [-f] [TARGET-PACKAGE]
       pm list features
       pm list libraries
       pm path PACKAGE
       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH
       pm uninstall [-k] PACKAGE
       pm clear PACKAGE
       pm enable PACKAGE_OR_COMPONENT
       pm disable PACKAGE_OR_COMPONENT
       pm disable-user PACKAGE_OR_COMPONENT
       pm set-install-location [0/auto] [1/internal] [2/external]
       pm get-install-location
       pm createUser USER_NAME
       pm removeUser USER_ID
  
pm list packages: prints all packages, optionally only
  those whose package name contains the text in FILTER.  Options:
    -f: see their associated file.
    -d: filter to only show disbled packages.
    -e: filter to only show enabled packages.
    -s: filter to only show system packages.
    -3: filter to only show third party packages.
    -u: also include uninstalled packages.
  
pm list permission-groups: prints all known permission groups.
  
pm list permissions: prints all known permissions, optionally only
  those in GROUP.  Options:
    -g: organize by group.
    -f: print all information.
    -s: short summary.
    -d: only list dangerous permissions.
    -u: list only the permissions users will see.
  
pm list instrumentation: use to list all test packages; optionally
  supply <target-package> to list the test packages for a particular
  application.  Options:
    -f: list the .apk file for the test package.
  
pm list features: prints all features of the system.
  
pm path: print the path to the .apk of the given PACKAGE.
  
pm install: installs a package to the system.  Options:
    -l: install the package with FORWARD_LOCK.
    -r: reinstall an exisiting app, keeping its data.
    -t: allow test .apks to be installed.
    -i: specify the installer package name.
    -s: install package on sdcard.
    -f: install package on internal flash.
  
pm uninstall: removes a package from the system. Options:
    -k: keep the data and cache directories around after package removal.
  
pm clear: deletes all data associated with a package.
  
pm enable, disable, disable-user: these commands change the enabled state
  of a given package or component (written as "package/class").
  
pm get-install-location: returns the current install location.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
  
pm set-install-location: changes the default install location.
  NOTE: this is only intended for debugging; using this can cause
  applications to break and other undersireable behavior.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
</target-package>

 

 

이 중 가장 많이 사용하는 명령어가 앱을 강제로 SD카드로 이동하는 명령어 일 겁니다. 해당 명령어도 아래와 같이 변경되었습니다.

설치 경로의 기본 값을 가져오는 명령어와 설정하는 명령어입니다.

 

pm get-install-location //기본값으로 지정된 설치 경로를 불러옵니다.
pm set-install-location 0/1/2 //강제로 경로를 설정합니다.

 

 

[adb를 이용한 apk 파일 추출 및 인스톨]

 

c:\> adb shell


# pm list packages -f

...

package:/system/app/SettingsProvider.apk=com.android.providers.settings
package:/system/app/TtsService.apk=android.tts
package:/system/app/Mms.apk=com.android.mms << 이걸 가져오고 싶으면...
package:/system/app/MediaProvider.apk=com.android.providers.media
package:/system/app/CertInstaller.apk=com.android.certinstaller
package:/system/app/DownloadProvider.apk=com.android.providers.downloads

...

 

# exit

 

c:\>adb pull /system/app/Mms.apk .

adb pull (가져올 파일 경로/파일명.apk) (받을 경로/파일명.apk)




출처 : 코드 클리핑 (http://www.dreamy.pe.kr/zbxe/CodeClip/163974)

1. Man In The Middle Attack?

  중간자 공격(man in the middle attack, MITM)은 네트워크 통신을 조작하여 통신 내용을 도청하거나 조작하는 공격 기법이다. 중간자 공격은 통신을 연결하는 두 사람 사이에 중간자가 침입하여, 두 사람은 상대방에게 연결했다고 생각하지만 실제로는 두 사람은 중간자에게 연결되어 있으며 중간자가 한쪽에서 전달된 정보를 도청 및 조작한 후 다른 쪽으로 전달한다.


  일반적으로 가정에서 흔히 구성되고 있는 네트워크 형태로는 하나의 인터넷 회선에 공유기를 사용하여 홈 네트워크를 구성하고 있다. 이 때, 공격자가 같은 홈 네트워크를 사용하고 있다고 가정하면, 위 그림과 같은 중간자 공격을 수행 할 수 있다. 피해자 입장에서는 공유기를 통해서 인터넷과 연결되고 생각 할 수 있지만, 실제로는 공격자를 통해 공유기를 거쳐 인터넷을 사용 하고 있는 것이고, 공격자는 피해자의 패킷들을 감청 및 조작이 가능하다.

  이 글에서는 ARP 스푸핑을 통해 MITM을 수행하는 모습을 보여줄 것이다.

실습 환경은 공격자 PCOSKali linux 2.0, 피해자 OSWindows 7을 사용하였다. APiptime 104N모델 펌웨어 9.58버전을 사용하였다.

 

2. ARP Spoofing

  ARP 스푸핑(ARP spoofing)은 근거리 통신망(LAN) 하에서 주소 결정 프로토콜(ARP) 메시지를 이용하여 상대방의 데이터 패킷을 중간에서 가로채는 중간자 공격 기법이다. 이 공격은 데이터 링크 상의 프로토콜인 ARP 프로토콜을 이용하기 때문에 근거리상의 통신에서만 사용할 수 있는 공격이다.

 이 기법을 사용한 공격의 경우 특별한 이상 증상이 쉽게 나타나지 않기 때문에 사용자는 특별한 도구를 사용하지 않는 이상 쉽게 자신이 공격당하고 있다는 사실을 확인하기 힘들다.

  ARP(Address Resolution Protocol, ARP)는 네트워크 상에서 IP 주소를 물리적 네트워크 주소로 대응(bind)시키기 위해 사용되는 프로토콜이다. 여기서 물리적 네트워크 주소는 이더넷 또는 토큰링의 48 비트 네트워크 카드 주소를 뜻한다.

 

3. arpspoof tool 사용 

victimip192.168.0.3이고, 네트워크의 gatewayip192.168.0.1,

공격자의 ip192.168.0.2이다. 

arpspoof 사용법 : arpspoof -i [interface 설정] -t [victim ip] [위장 할 ip] 

arpspoof -i wlan0 -t 192.168.0.3 192.168.0.1

 먼저, 피해자 pc에 내가 게이트웨이인 것처럼 위장하도록 한다.

 그 다음, 같은 방식으로 arpspoof를 이용해 게이트웨이에게 내가 피해자의 pc인 것처럼 위장한다.

arpspoof -i wlan0 -t 192.168.0.3 192.168.0.1

  이렇게 설정하면 위의 그림과 같은 가짜 경로를 만들어 낼 수 있으며, 실제로 피해자의 컴퓨터에서 arp -a 명령어를 통해 arp테이블을 보면 게이트웨이 ip주소와 공격자 컴퓨터의 맥주소가 같은 것을 볼 수 있다.

 

 피해자와 게이트웨이로부터 받은 패킷을 서로에게 ip forwarding 해주기 위하여 echo 1 > /proc/sys/net/ipv4/ip_forward 로 설정을 해준 후 fragrouter툴을 사용하여 ip 포워딩을 해서 연결을 완성해준다.

 

fragrouter -B1

 이 후, WireShark를 이용하여 피해자의 인터넷 패킷을 감청 할 수 있으며,

필터에 ip.addr == 192.168.0.3(피해자의 ip주소) 입력하여 필터링을 해주어 쉽게 볼 수 있다.

 

  아래의 그림은 한국기술교육대학교 온라인교육 홈페이지에 로그인 할 때의 패킷이고, 해당 홈페이지는 아이디와 비밀번호를 Plain text로 전달하여 다 보이게 된다.




1 . 정적분석

 

1) VirusTotal

분석 전 실습파일(Lab03-03.exe)를 VirusTotal(www.virustotal.com)에서 검사를 해본 결과

탐지 비율이 37/54로써 악성 코드임을 짐작할 수 있었다.


2) PEview와 Depandency Walker



 

 PEview와 Depandency Walker Lab03-03.exe PE구조에서 kernel.dll밖에 임포트하지 않는 것으로 보여지지만,

GetProcAddress 함수를 사용하는 것으로 보아서는 다른 dll도 임포트하지 않을까 라는 추측을 했고

WriteFile함수를 보고 어떠한 파일에 영향을 미치는 일을 할 것 같다는 생각을 했다.


3) PEiD

혹시 패킹이 된것은 아닐까 싶어 PEiD를 사용하여 분석해보았지만 

Visual C++ 6.0으로 컴파일 된 것 외에 다른 정보는 얻을 수 없었다.


 4) strings

전에 하던 정적 분석에서는 strings를 사용해 유용한 문자열을 많이 뽑아 볼 수 있었지만,

이번 파일은 그다지 유용한 문자열이 없었다. 거의 텍스트가 깨지거나 의미없는 특수기호 대문자 AAAAAAAAAAAA의 

연속이였고 그나마 의미있어보이는 문자열을 캡쳐해보았다. 물론 저게 무엇을 의미하는지는 잘 모르겠다.



 

정적 분석 결과 

1. virus total을 이용한 조회 결과는 악성코드로 보는 것이 맞는 것 같다.

2. 어떠한 방식인지는 모르겠지만 파일에 영향을 주는 일을 할 것같다.

-----------------------------------------------------------------------이외엔 정적 분석으로 별 소득이 없었다. 

 

2. 동적분석

1) Process Monitor

Lab03-03.exe를 실행 하면, svchost.exe라는 자식 프로세스를 생성 한 후에 

Lab03-03.exe프로세스는 종료되어진다.


생성된 자식 프로세스 svchost.exe는 Lab03-03.exe프로세스 종료 이후 고아 프로세스가 된다.

(책에서 고아 프로세스라고 설명을 하던데 원래 있는 말인지는 모르겠지만 잘 이해할 수있게 표현 한것 같다.)

svchost.exe는 아래와 같이 services.exe아래에 자식 프로세스로 생성 되는 것이 일반적이기 때문에

고아프로세스로 실행 되고 있는 svchost.exe프로세스는 이상하다고 생각 된다.

고아가 된 svchost.exe프로세스의 속성을 확인하던 중 [ENTER]와 [TAB]과 같은 문자열을 발견했는데,

보통 키로거에서 많이 볼 수 있는 문자열이라고 한다.

또한 practicalmalwareanalysis.log이라는 로그파일(?)의 이름처럼 보이는 문자열도 찾았다.

(키로거는 사용자의 키 값을 서버에 전송하거나, 키의 값을 파일로 저장하여 그 파일을 전송하는 악성코드의 종류인데,

아마 숫자 영문키 외에 엔터, 쉬프트, 컨트롤 등과 같은 키값을 저장하기 위해 만들어놓은 문자열이 아닌가 추측한다.) 


2) ProcessMonitor


 Process Explorer를 통해 수상한 프로세스를 찾았고, 그 프로세스의 pid는 3412였고, 

ProcMon에 필터를 PID로 두고 분석을하였다.

 분석을 한 결과 

Process Profiling

CreateFile

QueryStandardInformationFile

WriteFile (*n번)

CloseFile

IRP_MJ_CLOSE


1. Process Profiling을 하면서 키이벤트가 발생하는 것을 감지하고

2. CreateFile, QueryStandardInformationFile, WriteFile의 과정으로 파일을 열고 키값을 저장

3. CloseFile, IRP_MJ_CLOSE를 통해 파일입출력을 닫는다.

이러한 과정을 통해 키로깅을 하고 있는 것 같다.

파일의 저장경로는 옆에 나와있듯이 Lab03_03.exe가 위치한 폴더 내이고,

파일 명은 Process Explorer에서 찾아낸  practicalmalwareanalysis.log였다.


파일을 메모장으로 열어보니, 어떤 윈도우창에서 어떤 키를 조작했는지 까지 상세하게 나와있었다.

끝:)

------------------------------------------------------------------------------------------------------------

전에 했던 분석에서는 HTTP GET방식을 이용해 통신기능을 수행해서 탐지 시그니처를 정할 수 있었는데, 

이러한 경우엔 파일의 고유 값 외에 어떤 것을 탐지 시그니처로 정해야할지 아직 잘 모르겠다. ㅠ 

그래도 이번 분석은 책의 도움을 많이 받지 않고 한 것에 만족한다. 

 

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

Google I/O 2016 Extended Seoul 정리 및 후기  (4) 2016.06.19
Arpspoofing  (0) 2015.10.16
실전 악성코드와 멀웨어 분석 Lab03-02.dll  (0) 2014.08.29
실전 악성코드와 멀웨어 분석 Lab03-01.exe  (0) 2014.08.11
code engn 04  (0) 2014.04.04

1 . 정적분석

 

1) VirusTotal

분석 전 실습파일(Lab03-02.dll)VirusTotla(www.virustotal.com)에서 검사를 해본결과 

탐지 비율이 41/54로써 거의 악성코드임을 짐작할 수 있었다.

 

2) PEview




 PEviewLab03-02.dllPE구조에서, ws2_32.dllwinnet.dll을 임포트하며

사용하는 함수들을 보면 통신기능을 수행할 것으로 짐작돼는 internethttp관련 함수들이 보여서

어떠한 데이터를 외부로 전송하거나, 외부로 부터 악성코드를 다운받을 것이라 짐작되어진다.


 5) strings

strings로 찍어보면 practicalmalwareanalysis.com 이라는 서버네임같이 보이는 문자열도 보이고,

GET, HTTP/1.1이라는 문자열을 보아 유추해보면 80포트를 이용하여 httpget방식으로 사용할 것이라고 추측해 볼수 있었다. installA로 설치가 가능 할 것으로 예상된다.

 

정적 분석 결과 

1. 외부와 httpget방식을 이용하여 통신하여, 데이터를 전송하거나 악성코드를 다운받을 것이다. 2. installA라는 문자열을 통해 이것으로 설치를 할 수 있을 것 같다.

3. virus total을 이용한 조회 결과는 악성코드로 보는 것이 맞는 것 같다.

 


 

2. 동적분석

1) rundll32.exe Lab03-02.dll,installA

dll자체는 실행 파일이 아니므로 단독으로 실행하여 분석을 진행 할 수 없으나,

rundll32.exe을 통해 해당 dll의 함수를 실행 시켜 분석을 계속 진행 해 볼 수 있었다.

(dllinstallA실행 전후 관찰을 위해 rundll32실행 전 Process ExplorerRegshot을 미리 셋팅해두었고, 이 후 변화를 분석하였다.)

 

2) Regshot 




키가 추가된 위치를 보면 악성코드가 서비스 IPRIP를 이용해 설치 했다는 것을 볼 수 있다.

또한 악성코드가 dll이므로 자체 실행이 아닌 dll을 로드할 실행 파일이 필요한데 이것은 imagepath를 보면 알 수있다.

ImgePath는 헥사코드로 인코딩이 되어 있어서 디코딩을 하여 확인을 한 결과 

svchost.exe 프로세스 내에서 실행 되는 것을 확인 하였다.

(그 외 DisplayName이나 Description항목은 악의적인 서비스를 식별하는데 사용가능한 흔적을 생성한다. 라고 책에 설명되어 있는데, 아직은 잘 이해 하지 못하겠다.)


3) regedit



실행 -> regedit을 실행하여 실제 레지스트리에 저렇게 등록 되어 있는지 확인해 보았더니, regshot 보고서에서 보았던 

내용이 그대로 등록되어있음을 확인 할 수 있었다.


4) Process Explorer


Process Explorer를 통해 실제 Lab03-02.dll을 로드하여 실행되고있는 svchost를 구별하기 위해 검색해 보았고, 1104라는 pid를 가진 scvhost프로세스라는 것을 찾았다! 

(서비스에 Intranet Network Awaeness(INA+)[IPRIP]라는 항목도 보인다.)




5) 악성코드의 실행


ApateDNS를 통해서 모든 요청을 나 자신으로 돌아오게 만들고 넷켓을 이용하여 80포트로 들어오는 요청을 받는다.

특별히 80포트를 조회한 이유는 정적분석(strings)을 통하여 http의 GET방식을 사용 할 것이라 추측하였기때문이다.

그 결과 예상대로 http의 GET방식을 사용한 요청이 들어왔으며 host역시 정적분석 문자열을 통하여 확인했던

practicalmalwareanalysis.com 이였다.

여기서 악성코드의 시그니쳐를 뽑아 내자면,

첫째로 serve.html이 http요청을 하고있기 때문에 이것을 시그니쳐로 GET요청을 사용 할 수 있고,

두 번째로 User-Agent에서 사용자 컴퓨터명을 제외한 Windows XP 6.11이 일정하기때문에 시그니쳐로 사용 할 수 있다. 

:) 끝

____________________________________________________________________________________________________________

사실 아직까지는 책과 인터넷 검색을 통해서 따라하는 수준이고, 익숙치가 않다. 마지막 결과를 받는 부분에서 제대로 

악성코드를 설치하고 실행을 했음에도 결과값이 나오지않다가 어쩌다가 결과 값을 받게 되었다. 요청이 찍히지 않았는지, 왜 갑자기 요청이 들어오게 되었는지 잘모르겠지만, 하나하나 따라하다보면 결국엔 혼자서도 잘 할 수 있을 거라 믿고

열심히 진행해야겠다.


 

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

Arpspoofing  (0) 2015.10.16
실전 악성코드와 멀웨어 분석 Lab03_03.exe  (0) 2014.09.01
실전 악성코드와 멀웨어 분석 Lab03-01.exe  (0) 2014.08.11
code engn 04  (0) 2014.04.04
IA-32 Register 기본 설명 - 1  (0) 2013.10.08

1 . 정적분석


1) VirusTotal

분석 전 실습파일(Lab03-01.exe)을 VirusTotla(www.virustotal.com)에서 검사를 해본결과 

탐지 비율이 50/54로써 거의 악성코드임을 알수 있었다.


2) PEview

PEview로 Lab03-01.exe의 PE구조를 살펴 보니, 임포트한 dll은 kernel32.dll밖에 보이지 않았으며 별다른 점은 보이지 않았다.


3)Depandancy Walker

Depandancy Walker에서 살펴보아도 임포트한 dll은 kernel32.dll, 사용한 함수는 ExitProcess밖에 보이지 않는다.

이 점을 미루어보아 무언가로 패킹되었다는 것을 짐작 할 수 있었다.


4)PEiD


PEiD로 확인해 보니 예상대로 패킹이 되어있었고, PEncrypt로 패킹이 되어있었다. 


5) strings

strings로 찍어보면 www.practicalmalwareranalysis.com이라 던가 어떤 레지스트리 경로처럼 보이는 것도 나오고, 

의심스러운 파일명하며, 단서가 될만한 것들이 찍혀보이는 것을 확인했다.


정적 분석 결과 

PEncrypt로 패킹이 되어 있으며, 도메인네임이 있는 것으로 보아 통신기능(어떠한 파일을 다운을 받거나, 데이터를 빼내는 기능 등)을 하는 것 같다는 것을 유추해 볼 수 있다. 또한 무슨일을 하는지는 모르지만 VirusTotal의 결과로 사용자에게 피해를 주는 악성코드라는 것을 알게 되었다.


2. 동적분석

1)Process Explorer

프로그램 실행 전 Process Explorer를 실행하고 실습파일을 실행하였고, 


어떤 dll을 로드하는지 확인해보니 ws2_32.dll과 wshtcpip.dll과 같은 dll을 로디해서 사용하는것을 확인, 고로

통신기능을 수행할 것으로 생각 할 수 있다.


2)Process Monitor

위의 정적분석 (strings의 결과)를 거쳤을때, 어떠한 파일경로로 의심되는 문자열이 있었고, 레지스트리경로로 보이는 문자열이 있었기 때문에, 필터설정을 위의 그림과 같이 Process Name은 Lab03-01.exe, Operate는 writeFile과 RegSetValue를 필터링 하여 살펴본 결과가 나왔다. 

먼저, WriteFile의 결과로 나온것을 더블클릭하여 자세히보니,  c:\WINDOWS\system32의 경로에 

크기가 7168byte짜리인 vmx32to64.exe라는 파일을 생성하고 있다는 것을 알 수 있었다.

이 파일을 다시 VirusTotal에 올려서 검사를 해보았더니

아래 처럼 사실 파일의 이름만 바뀌었을 뿐, Lab03-01.exe와 같은 파일임을 알수 있었다. 위의 SHA256키가 같고,

다른 분들이 분석한 것을 보면 파일의 md5해시값도 또한 같다고 한다.

이 점을 보면 Lab03-01.exe를 실행하면 c:\WINDOWS\system32의 경로에 vmx32to64.exe라는 이름으로 바꾸어 

복사 생성한다는 것을 알 수있다.


다시 ProcMon의 결과로 돌아와서 이번엔 RegSetValue의 결과를 상세히 확인하면, 복사생성한 vmx32to65.exe를 

레지스트리에 등록하여 윈도우 시작시 프로그램이 자동 시작되도록 값을 셋팅하는 것을 알 수 있다. 

시작프로그램에서 역으로 제거하는 방법은 

실행창에서 regedit을 실행 -> 레지스트리 관리창을 띄우고

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run에서 해당 정보를

(여기서는 VideoDriver라는 이름으로 되어있다. 실제 실행되는 파일은 vmx32to64.exe이다.) 삭제한다.


3)ApateDNS

역시 정적분석(Strings)결과 도메인네임으로 보이는 문자열이 보였기때문에, 또한 Process Explorer의 결과로 

통신기능을 지원하는 dll을 로드하므로 통신기능을 수행할 것 같다는 짐작을 할 수 있었으므로, ApateDNS로 확인해보았더니 문자열에서 보았던 주소로 어떠한 요청을 보내고 있다는 것을 확인할 수 있었다.


따라서 요청하는 IP를 나에게 다시 돌아오게 만들고, netcat을 이용, 80번 포트(http서비스)와 443번 포트(https서비스)를 리스닝을해놓고 기다려보니 80번 포트는 아무것도 오지않았고, 443포트에서 알수 없는 외계어가 들어왔다.

확실히 어떠한 것을 보내고 있는지는 모르지만 해당 서버로 어떠한 데이터를 주기적으로  데이터를 전송하고 있었다.


최종결론

실습파일(Lab03-01.exe)은 실행시 악성코드작성자가 코딩한 경로로 이름을 변경한(vmx32to64.exe) 자기자신을 복사 생성하며, 동시에 레지스트리에 그 복사 파일을 등록하여 윈도우시작시 자동 실행되어지게 만들었다.

또한 어떠한 내용인지 정확히 밝혀지지는 않았지만 www.practicalmalwareanalysis.com으로 주기적으로 https를 이용 어떠한 데이터를 전송하고 있는 것으로 분석 되었다.







Q. This program can detect debuggers. Find out the name of the debugger detecting function the program uses.


그림 1

최초 프로그램을 실행해보면, 정상이라는 글이 일정 시간간격으로 출력되는 것을 볼 수있다.



 그러나, 올리디버거로(또는 다른 디버깅 툴) 실행시,

그림 2

이러한 메인 루틴을 지나게 되면


그림 3

디버깅 당하고 있음을 출력하는 문자열을 볼 수 있다.


메인 루틴(그림 2)을 살펴 보면 문제에서 요구하는 답을 직관적으로 (IsDebuggerPresent) 알 수 있으며, 일정 시간을 두면서 문자열을 출력하는 이유 또한 알 수 있다.(Sleep함수)

보이는 바와 같이 IsDebuggerPresent함수는 Kernerl32.dll에 속해 있고, 디버깅을 당하고 있을시 non-zero, 디버깅을 당하고 있지 않다면 zero를 리턴한다. 

MSDN참조 : http://msdn.microsoft.com/en-us/library/windows/desktop/ms680345(v=vs.85).aspx


문제에 대한 답은 찾았으나, 디버깅을 하면서도 정상 이라는 문자열을 출력해보고 싶었다.

가장 쉽게 처음 생각한 방법은 IsDebuggerPresent함수를 지나 후에 있는 

TEST eax,eax 를 mov eax, 0으로 바꾸어보는 것이였다. 리턴 값을 조정해주면 내부가 어떻든간에

쉽게 해결될꺼라고 생각했는데, 정말 멍청한 생각이였음.

test 명령어는 두 값을 비교하여 플래그를 수정한다.

TEST 명령어는 피연산자들을 AND연산하여 즉, 두 피연산자중 하나라도 0이면 ZF = 1로 셋팅된다.

따라서 test eax, eax라는 것은 eax가 0인지 아닌지를 판별하는 명령어를 의미하게 된다.

CMP명렁어와 다른점은 두 피연산자들의 뺄셈연산을하여 같은지 다른지를 비교하는 것으로 차이점이 있다.

test명령어 아래에 나타나는 JE명령어는 이 ZF가 1이면 점프분기하는 조건분기 명령어중 하나이다.

따라서 단순 mov eax, 0으로 바꾸게 된다면 ZF값을 수정해 주지 못함으로써 아래의 JE조건분기를 수행할 수 없어서

프로그램이 뻗게되었다.

다음 방법으로 생각 한 것은 IsDebuggerPresent함수 내부에서 리턴 하기 전에 리턴 값을 0으로 수정해보기로 하였다.

함수 내부로 진입해 점프를 두 번 수행 하고 나니


그림 4

이 세개의 명령어 수행후 리턴을 하게 되었다. 이때 eax값은 7efde000, 따라서 7efde002 메모리번지에 있는 값으로 셋팅된 후 리턴 하게 된다는 것을 알게 되었고, 이 메모리 번지로 찾아갔다.


그림 5

BeingDebugged = TRUE로 되어있는 것을 보면 이 값을 리턴되었을때 디버깅이 되고있음을 의미한다는 것을 확실히 확신할 수 있었지만, 이 값을 변경 할 수 없었으며,

반대의 의미를 리턴하는 값을 아래에서 찾을 수 없었다.(찾게 된다면 74f33785의 movzx문을 수정해볼 생각이였다.)

왜 변경 할 수 없었는지에 대한 의문.... 잘 모르겠다.

kernerl32.dll에 있기 때문이거나, dll에 있는 부분이여서 동적으로 할당 되는 부분을 수정할 수 없기 때문이 아닌가 하고 생각만 해봄...ㅠㅠ


결국 IsDebuggerPresent함수 내부를 수정하는 것도 실패하고, 남은 방법은 

메인 루틴에서 test명령어 이후에 나타나는 je명령어를 jmp로 수정하는 방법밖에 떠오르지 않아서, 수정해보았더니

디버깅 중에도 정상으로 출력하는 것을 볼 수 있었다. 



결국 돌아돌아서 별 거 아닌 패치를 했지만, IsDebuggerPresent함수 내부를 한번 들여다 본 것과

단순 레지스터들의 값들뿐만아니라 여러 플래그들도 같이 둘러봐야 된다는 교훈을 얻게된 것같다.





CPU Register란?

: CPU내부에 존재하는 다목적 공간

  RAM은 물리적 제약에 의해 비교적 저속이나,

  Register는 CPU와 한 몸이기 때문에 고속이다.

  ( Register >> Cache >> RAM >> HDD ) //컴구조 시간에 배운 기억이;;


레지스터의 종류는 많지만, 우선은 기본적으로 알아야할 레지스터부터 알아본다.

*추후 control, memory, debug Register들의 공부도 필요하게 될 것같다.


Basic Program execution Register  - General Purpose Registers ( 32bit * 8개 ) //범용레지스터

                                                 - Segment Registers ( 16bit * 6개 )

                                                 - Program Status Control Registers ( 32bit * 1개 )

                                                 - Instruction Pointer ( 32bit * 1개 ) 


먼저 General Purpose Registers에 대해 알아보면,

각각의 크기는 32bit ( 4 byte )이며, 보통의 용도는 상수/주소 등을 저장하는데 쓴다.

특정 어셈블리 명령어에서 특정레지스터를 조작하기도하며, 특정레지스터는 특수용도로 사용된다.


그림과 같이 범용레지스터는

EAX ( Accumulator Operands and result data )

EBX ( Pointer to data in the DS Segment )

ECX ( Counter for String and Loop Operations )

EDX ( I/O Pointer )

EBP ( Pointer to data on the Stack )

ESI ( Source Pointer for String Operations )

EDI ( Destination Pointer for string Operations )

ESP ( Stack Pointer ) 로 구성이 되어있다.

그림에서 반이 나누어진 것을 볼수 있는데,

현재 사용되고있는 32bit기준으로 레지스터가 확장되었기 때문이다.

예전에는 16bit를 사용 하였기때문에 레지스터의 용량 또한 16bit였고, 여기서

확장된(Extend)의미를 붙여 각 레지스터에 E가 붙게 되었다.

새로 레지스터를 만들지 않고 확장시킨이유는 예전과 호환을 위해서라고 한다.


각 범용레지스터의 사용 용도는

EAX ~ EBX레지스터들은 주로 산술연산 (add, sub, xor, or 등) 명령어에서 상수/변수 값의 저장용도로

많이 사용되며 특별히 EAX와 ECX는 다른 특정 용도가 있다.

eax -> 함수 리턴값에 사용             ecx-> 반복loop 카운트수 저장

ESP는 스택 메모리 주소를 저장하고 있어서, 스택의 PUSH, POP에 따라 값이 변한다.

EBP는 함수 호출시 그 순간의 ESP를 저장하여 리턴 직전에 ESP값을 되돌려서 Stack이 깨지지않게 하는 역할을 한다.

나중에 나오지만 이것을 Stack Frame 기법이라고 한다.

ESI와 EDI는 특정 명령어들과 함께 주로 메모리 복사에 사용된다.



출처 : 리버싱 핵심원리 (이승원 저)



같은 네트워크망에 있다면 아이피 형식은 ex) 192.168.0.x 이런식으로 비슷하기 마련이다.

보통은 1은 게이트웨이로 쓰이며 이후 255까지 많은 접속 컴퓨터및 통신기기가 있을 수 있다.

먼저 기본으로 자신의 ip는 ifconfig 명령어를 통해 ip address 및 게이트웨이주소 맥주소 등을 확인해 볼수 있다.

그리고 ping 명령어를 통해 해당 아이피주소에 통신기기의 응답을 받을 수 있다.

예를 들어 ping 192.168.0.2 ~ 192.168.0.255 까지 각 아이피 주소에 대해 접속한 기기를 검색해 볼수 있는데,

하나하나 다 하다보면 시간이 너무 오래걸릴 것이므로....

간단한 프로그래밍을 통해 스스로 핑을 확인해 볼 수 있다.


#include <stdio.h>

int main()

{

      int i = 0;

      char order[100];

      for( i = 0; i <= 255; i++)

     {

            sprintf( order, "ping -c 192.168.0.%d |grep from", i );

            system( order );

     }


     printf("Search is finished\n");

  

     return 0;

}


위의 예제를 보면 0부터 255까지 i가 증가하면서  ping 명령어를 찍어보고 있고, 255까지 서치가 끝나면 서치가 끝났다는

printf문을 끝으로 프로그램이 종료된다.

ping명령어의 옵션으로 -c를 붙인 것은 해당 아이피의 응답을 한 번만 듣겠다는 옵션이고,

|grep from은 from이 포함된 결과만 보게 해주는 구문이다. 왜 그렇게 했냐면

응답이 있는 아이피 어드레스만이 from이 포함된 출력을 주고,(응답이 없을 경우 from이 포함되어있지않은 결과가나옴)

우리가 얻고 싶은 아이피 어드레스는 응답이 있는 것이므로 쓸데 없는 결과를 삭제한다고 볼 수 있다.

참조 : 해커스쿨 (http://www.hackerschool.org/)

처음으로 블로그에 올리는 글이고, 아직 학부생이라 허접한 내용이지만 하나씩 하나씩 글쓰는 재미를 얻어가야겠다;;;



+ Recent posts