17th 2018 PoC 동계 해킹캠프 PPAP Writeup

Writeup Jun 19, 2018

인사말

안녕하세요 리버싱 PPAP 문제 출제자 SHGroup 입니다!

이 문제는 Java 로 지금까지 개발을 해오면서 사용했던 안티 디컴파일 방지 기술을 사용하여 냈던 문제입니다.

이 문제를 통해 Java 안티 디컴파일링 기법에 대해 배워가셨으면 좋겠습니다!

이제 라업 시작합니다!

문제 다운로드

다운로드

분석 과정

문제에서 PPAP.jar 파일을 줍니다.

이 파일은 JAR(Java Archive File) 파일로써, Java에서 클래스파일, 메타파일, 리소스파일 등 실행에 필요한 파일들을 압축해놓은 배포용 패키지 파일입니다.

이 파일을 보통 리버싱하기 위해서는 jad(또는 GUI 래핑 버젼인 jd-gui)를 사용하는데, jd-gui 로 이 파일을 열게 되면

jd-gui-1

이렇게 패키지는 잘 나오지만, 막상 클래스 파일을 열게 되면

jd-gui-2

다음과 같이 오류가 나게 됩니다.

jd-gui 는 Java 7 로 만들어졌는데, 이 파일은 Java 8 함수를 사용하여 바이트코드를 계속 읽지 못해서 일어나는 현상입니다.

해결법은 간단합니다. Java 8 을 지원하는 디컴파일러로 여는 것입니다. 저는 luyten 을 사용하였습니다.

luyten-decompile

소스가 나왔습니다.

package com.SHGroup.PPAP;

import java.util.*;
import java.lang.reflect.*;

public class EntryMain
{
    public static void main(final String[] args) {
        final Class<?> ppap = new ClassLoader(EntryMain.class.getClassLoader()) {
            public Class<?> getCustomClass() {
                final byte[] bs = { -54, -2, -70, -66, 0, 0, 0, 52, 0, 71, 7, 0, 2, 1, 0, 4, 80, 80, 65, 80, 7, 0, 4, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 10, 0, 3, 0, 9, 12, 0, 5, 0, 6, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 6, 76, 80, 80, 65, 80, 59, 1, 0, 4, 112, 112, 97, 112, 7, 0, 16, 1, 0, 17, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 83, 99, 97, 110, 110, 101, 114, 9, 0, 18, 0, 20, 7, 0, 19, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 12, 0, 21, 0, 22, 1, 0, 2, 105, 110, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 10, 0, 15, 0, 24, 12, 0, 5, 0, 25, 1, 0, 24, 40, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 41, 86, 10, 0, 15, 0, 27, 12, 0, 28, 0, 29, 1, 0, 8, 110, 101, 120, 116, 76, 105, 110, 101, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 10, 0, 15, 0, 31, 12, 0, 32, 0, 6, 1, 0, 5, 99, 108, 111, 115, 101, 10, 0, 34, 0, 36, 7, 0, 35, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 12, 0, 37, 0, 38, 1, 0, 6, 108, 101, 110, 103, 116, 104, 1, 0, 3, 40, 41, 73, 10, 0, 34, 0, 40, 12, 0, 41, 0, 42, 1, 0, 6, 99, 104, 97, 114, 65, 116, 1, 0, 4, 40, 73, 41, 67, 9, 0, 18, 0, 44, 12, 0, 45, 0, 46, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 8, 0, 48, 1, 0, 15, 67, 79, 82, 82, 69, 67, 84, 32, 68, 65, 71, 65, 78, 89, 65, 10, 0, 50, 0, 52, 7, 0, 51, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 12, 0, 53, 0, 54, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 8, 0, 56, 1, 0, 12, 84, 82, 89, 32, 65, 71, 65, 73, 78, 46, 46, 46, 1, 0, 7, 115, 99, 97, 110, 110, 101, 114, 1, 0, 19, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 83, 99, 97, 110, 110, 101, 114, 59, 1, 0, 5, 105, 110, 112, 117, 116, 1, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 7, 115, 117, 99, 99, 101, 115, 115, 1, 0, 1, 73, 1, 0, 4, 97, 114, 114, 115, 1, 0, 2, 91, 73, 1, 0, 1, 105, 1, 0, 3, 110, 111, 119, 1, 0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 7, 0, 64, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 80, 80, 65, 80, 46, 106, 97, 118, 97, 0, 33, 0, 1, 0, 3, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 8, -79, 0, 0, 0, 2, 0, 10, 0, 0, 0, 6, 0, 1, 0, 0, 0, 5, 0, 11, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 12, 0, 13, 0, 0, 0, 1, 0, 14, 0, 6, 0, 1, 0, 7, 0, 0, 2, -30, 0, 7, 0, 7, 0, 0, 1, -39, -69, 0, 15, 89, -78, 0, 17, -73, 0, 23, 76, 43, -74, 0, 26, 77, 43, -74, 0, 30, 3, 62, 16, 56, -68, 10, 89, 3, 16, 75, 79, 89, 4, 16, 68, 79, 89, 5, 16, 74, 79, 89, 6, 16, 66, 79, 89, 7, 16, 67, 79, 89, 8, 16, 108, 79, 89, 16, 6, 16, 75, 79, 89, 16, 7, 16, 79, 79, 89, 16, 8, 16, 115, 79, 89, 16, 9, 16, 119, 79, 89, 16, 10, 16, 123, 79, 89, 16, 11, 16, 127, 79, 89, 16, 12, 16, 99, 79, 89, 16, 13, 16, 103, 79, 89, 16, 14, 16, 107, 79, 89, 16, 15, 16, 111, 79, 89, 16, 16, 5, 79, 89, 16, 17, 16, 23, 79, 89, 16, 18, 16, 27, 79, 89, 16, 19, 16, 31, 79, 89, 16, 20, 6, 79, 89, 16, 21, 16, 7, 79, 89, 16, 22, 16, 11, 79, 89, 16, 23, 16, 15, 79, 89, 16, 24, 16, 51, 79, 89, 16, 25, 16, 55, 79, 89, 16, 26, 16, 59, 79, 89, 16, 27, 16, 63, 79, 89, 16, 28, 16, 35, 79, 89, 16, 29, 16, 39, 79, 89, 16, 30, 16, 36, 79, 89, 16, 31, 16, 47, 79, 89, 16, 32, 17, 0, -80, 79, 89, 16, 33, 17, 0, -55, 79, 89, 16, 34, 17, 0, -44, 79, 89, 16, 35, 17, 0, -33, 79, 89, 16, 36, 17, 0, -94, 79, 89, 16, 37, 17, 0, -39, 79, 89, 16, 38, 17, 0, -88, 79, 89, 16, 39, 17, 0, -64, 79, 89, 16, 40, 17, 0, -105, 79, 89, 16, 41, 17, 0, -9, 79, 89, 16, 42, 17, 0, -5, 79, 89, 16, 43, 17, 0, -29, 79, 89, 16, 44, 17, 0, -10, 79, 89, 16, 45, 17, 0, -24, 79, 89, 16, 46, 17, 0, -113, 79, 89, 16, 47, 17, 0, -17, 79, 89, 16, 48, 17, 0, -109, 79, 89, 16, 49, 17, 0, -117, 79, 89, 16, 50, 17, 0, -8, 79, 89, 16, 51, 17, 0, -112, 79, 89, 16, 52, 17, 0, -125, 79, 89, 16, 53, 17, 0, -28, 79, 89, 16, 54, 17, 0, -107, 79, 89, 16, 55, 17, 0, -94, 79, 58, 4, 44, -74, 0, 33, 25, 4, -66, -97, 0, 8, 4, 62, -89, 0, 57, 3, 54, 5, -89, 0, 42, 44, 21, 5, -74, 0, 39, 54, 6, 29, 25, 4, 21, 5, 46, 21, 6, 21, 5, 6, 21, 5, 4, 96, 104, 96, -126, -97, 0, 7, 4, -89, 0, 4, 3, 96, 62, -124, 5, 1, 21, 5, 44, -74, 0, 33, -95, -1, -45, 29, -102, 0, 14, -78, 0, 43, 18, 47, -74, 0, 49, -89, 0, 11, -78, 0, 43, 18, 55, -74, 0, 49, -79, 0, 0, 0, 3, 0, 10, 0, 0, 0, 82, 0, 20, 0, 0, 0, 7, 0, 11, 0, 8, 0, 16, 0, 9, 0, 20, 0, 10, 0, 22, 0, 11, 0, -109, 0, 12, 1, 41, 0, 13, 1, 122, 0, 11, 1, 124, 0, 14, 1, -122, 0, 15, 1, -120, 0, 16, 1, -117, 0, 17, 1, -111, 0, 18, 1, -103, 0, 19, 1, -75, 0, 17, 1, -63, 0, 22, 1, -59, 0, 23, 1, -51, 0, 24, 1, -48, 0, 25, 1, -40, 0, 27, 0, 11, 0, 0, 0, 72, 0, 7, 0, 0, 1, -39, 0, 12, 0, 13, 0, 0, 0, 11, 1, -50, 0, 57, 0, 58, 0, 1, 0, 16, 1, -55, 0, 59, 0, 60, 0, 2, 0, 22, 1, -61, 0, 61, 0, 62, 0, 3, 1, 124, 0, 93, 0, 63, 0, 64, 0, 4, 1, -114, 0, 51, 0, 65, 0, 62, 0, 5, 1, -103, 0, 28, 0, 66, 0, 62, 0, 6, 0, 67, 0, 0, 0, 81, 0, 8, -1, 1, -117, 0, 5, 7, 0, 1, 7, 0, 15, 7, 0, 34, 1, 7, 0, 68, 0, 0, -4, 0, 5, 1, -1, 0, 32, 0, 7, 7, 0, 1, 7, 0, 15, 7, 0, 34, 1, 7, 0, 68, 1, 1, 0, 1, 1, -1, 0, 0, 0, 7, 7, 0, 1, 7, 0, 15, 7, 0, 34, 1, 7, 0, 68, 1, 1, 0, 2, 1, 1, -6, 0, 4, -6, 0, 8, 14, 7, 0, 1, 0, 69, 0, 0, 0, 2, 0, 70 };
                return super.defineClass("PPAP", bs, 0, bs.length);
            }
        }.getCustomClass();
        try {
            final Object ins = ppap.newInstance();
            final Method m = ppap.getDeclaredMethod("ppap", (Class<?>[])new Class[0]);
            m.setAccessible(true);
            m.invoke(ins, new Object[0]);
        }
        catch (Exception e) {
            final ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(0, 5, 10));
            list.stream().filter(num -> num % 2 == 0);
            e.printStackTrace();
        }
    }
}

음? 뭔가 이상한 것 같네요..?

분명 이 파일을 실행하면 다음과 같이 입력을 받고 검사를 합니다.

run_file

근데 저 문자열은 물론 비교하는 구문마저 나오지가 않습니다 ㅠㅠㅠ...

? 그럼 어떻게 해야되는거지?

사실 이 프로그램의 구조는 다음과 같습니다.

  1. 클래스 파일을 메모리에 로드한다.
  2. 클래스 파일에서 PPAP 라는 클래스를 가져온다.
  3. 그 클래스에서 ppap 메소드를 호출한다.

말은 거창하게 써놨지만, 결론은 저 바이트 배열이 바로 클래스파일 이라는 것입니다!

(더 자세히 알아보고 싶으시다면 Java ReflectionClass Loader 에 대해 공부해보세요!)

(저기 catch 블럭에서 사용되는 ArrayList 의 stream 기능이 Java 8 에서 추가된 기능입니다. 이 문제에서는 Java 8의 기능은 사용하지 않았습니다.)

이제 저 바이트 배열을 클래스파일로 따로 뽑아서 분석하면 이 문제를 풀 수 있을 것입니다.

이 배열을 파일로 만들기 위해 다음과 같이 소스를 짰습니다.

import java.io.File;
import java.io.FileOutputStream;

public class solve {
    public static void main(String[] args){
        byte[] bs = { /* byte array */ };
        try{
            File f = new File("ppap.class");
            f.createNewFile();

            FileOutputStream fos = new FileOutputStream(f);
            fos.write(bs);
            fos.close();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

그리고 컴파일하고 실행합니다.

compile

그러면 다음과 같이 ppap.class 파일이 추출됩니다.

ppap.class

이제 이 파일을 디컴파일하면

ppap.java

정상적으로 소스를 분석할 수 있는 상태가 됩니다.

소스는 다음과 같습니다.

import java.util.*;

public class PPAP
{
    public void ppap() {
        final Scanner scanner = new Scanner(System.in);
        final String input = scanner.nextLine();
        scanner.close();
        int success = 0;
        final int[] arrs = { 75, 68, 74, 66, 67, 108, 75, 79, 115, 119, 123, 127, 99, 103, 107, 111, 2, 23, 27, 31, 3, 7, 11, 15, 51, 55, 59, 63, 35, 39, 36, 47, 176, 201, 212, 223, 162, 217, 168, 192, 151, 247, 251, 227, 246, 232, 143, 239, 147, 139, 248, 144, 131, 228, 149, 162 };
        if (input.length() != arrs.length) {
            success = 1;
        }
        else {
            for (int i = 0; i < input.length(); ++i) {
                final int now = input.charAt(i);
                success += ((arrs[i] != (now ^ i + 3 * (i + 1))) ? 1 : 0);
            }
        }
        if (success == 0) {
            System.out.println("CORRECT DAGANYA");
        }
        else {
            System.out.println("TRY AGAIN...");
        }
    }
}

마지막으로 파이썬으로 역연산을 해주면

import sys
arr = [75, 68, 74, 66, 67, 108, 75, 79, 115, 119, 123, 127, 99, 103, 107, 111, 2, 23, 27, 31, 3, 7, 11, 15, 51, 55, 59, 63, 35, 39, 36, 47, 176, 201, 212, 223, 162, 217, 168, 192, 151, 247, 251, 227, 246, 232, 143, 239, 147, 139, 248, 144, 131, 228, 149, 162]
for i, c in enumerate(arr):
    sys.stdout.write(chr(c ^ i + 3 * (i + 1)))
print("")

final_solve

플래그가 나옵니다!!

또한 원래 jar 파일에서 이 플래그를 입력하면 맞았다고 뜹니다!

correct_daganya

플래그

HCAMP{PPPPPPPPPPAPPPPPPPPPPPPP_P3N_P1N3_4PPLE_4PPL3_P3N}

Tags

Sunghun Kim

Sunghun Kim (a.k.a. SHGroup, KSHGroup)

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.