리버싱 8차시

k4bunny·2025년 7월 6일

Layer7

목록 보기
11/13

Layer7 CTF

Custom 1

문제 파일을 다운로드 받고 IDA로 컴파일하였다.

다음의 코드의 상단부터 천천히 확인해보면
vars0이라는 80길이의 문자열을 선언하고 s라는 문자열에 값을 입력받는 것으로 추정해볼 수 있다.
v4에다가는 정수 25를 대입한다.
반복문에서는 vars0의 값에 키 값인 enc1_0을 이용해 암호화한 코드를 대입하는 것을 알 수 있다.
그 후, 입력받은 s의 값과 암호화된 vars0의 값을 비교하여 일치 할 경우 "Correct!"가 출력된다.

먼저 fgets를 이용하여 값을 입력받고 암호화하여 key값과 비교하는 것이 아닌 key값을 암호화 해제하여 입력받은 값과 비교하여 "correct!"를 출력하는 것이기 때문에 위 코드에 vars0에 저장하는 과정이 바로 flag 값을 만드는 과정인것이다.

따라서 vars0에 대입하는 코드 부분을 그대로 따라쓰면 우리는 enc1_0 배열의 값들을 모두 알고 있기 때문에 flag를 구할 수 있다.

#include <stdio.h>

int main() {
    int v3;
    int v6;
    int v7;
    int v8; 
    char vars0[80];
    unsigned enc1_0[64] = {
        0x19, 9,    0x4E, 0xB7, 0xD3, 0x83, 8,    0x6D, 0x9A, 0x89, 0x8C,
        0x33, 0xE,  0x92, 0xCD, 0xA2, 0xD2, 0x20, 0x8C, 0x8B, 0x96,
        0x95, 0x56, 0xA7, 0x8A, 0xB2, 0xC7, 0x0F, 0xAA, 0x8E, 0x82,
        0xEC, 0x26, 0x4A, 0xBE, 0xAB, 0x96, 5,    0x74, 0xBA, 0xCF,
        0xB8, 0x33, 0x78, 0x80, 0xBE, 0x91, 0x2C, 0x17, 0xC8, 0x60,
        0x86, 0x43, 0x28, 0x33, 0x6F, 0xAE, 0xC2, 0x7A, 0x68, 0x2A,
        0xB9, 0xA4, 0x2B
    };

    int v4 = 25;
    v3 = 0;

    for (int i = 0; v4 = enc1_0[i]; i++) {
        v6 = v3;
        v3 = (unsigned int)(v3 + 51);
        v7 = v4 ^ ((v6 ^ 0x55u) + 2 * i);
        vars0[i] = v7;
    
        if ( i == 64 ) 
            break;
    }
    
    vars0[64] = 0;

    for (int i = 0; i < 64; i++) {
        printf("%c", vars0[i]);
    }
}

Custom 2

문제 파일을 다운받아 컴파일 해보면

이러한 main함수를 알 수 있다.
입력받은 문자열 s를 인자로 가지는 validate_input 함수를 확인해보면

"this is not the way" 라는 문구가 있는걸 보니 이 함수는 가짜인 것 같다.
전체적으로 둘러보기 앞서 먼저 함수 목록을 살펴보았다.

print_flag라는 함수가 너무 수상해보여서 일단 먼저 확인해보았다.


누가봐도 진짜같아 보이는 암호화 함수를 발견했다.
다른 것도 둘러보았지만 이 함수만큼 확실해보이는 것은 없었다.
해당 코드를 분석해보면 여러가지 함수를 호출하여 암호화를 실행하고 최종적으로 완료된 문장을 puts를 통해 출력하는 코드였다.

inv_p(n) 역시 가짜함수가 아니였다.
모든 코드에 대한 풀이 소스를 짜는 것은 무리가 있어보였기에 동적분석을 하기로 선택했다.

다시 처음부터 생각해보면 이 코드는 main함수에서 validate_input 함수를 이용하여 가짜 검사를 진행한다.

이때 생각을 뒤집어서 해보면 가짜 검사를 하는 validate_input 함수 대신 print_flag 함수를 호출하면 flag 값을 알아낼 수 있을 것이라고 생각했다.

생각을 토대로 바로 실행해주었다.

잘 바뀐 것을 확인하고 난 후, gdb를 가서 해당 파일을 실행시켜주었다.

Enter password는 validate_input 함수의 인자를 입력받는 함수였으므로, 이제는 사실상 의미가 없는 함수니 무시하고 아무거나 입력해줬다.

정상적으로 print_flag 함수가 작동하며 플래그 값을 얻었다.

Dreamhack Reversing

Dreamhack Wargame

legacyopt


문제 파일은 legacyopt와 output 두 개가 주어진다.
output은 Base64로 암호화된 플래그로 추정되는 문자열이 담겨있다.

legacyopt를 IDA로 디컴파일 해보았다.

문자열을 입력받아 s에 저장하고 ptr과 함께 sub_1209에 인자로 보낸 후,
2자리 Hex값으로 ptr을 출력한다.
sub_1209를 분석하여 이 프로그램이 무엇을 하는 프로그램인지 분석해보았다.



a3의 길이를 8로 나눈 나머지에 따라 switch문을 거치는 것을 그대로 구현하면 될 것 같다.
각 label이 모두 특정한 값과 XOR을 하는 모두 똑같은 구조를 가지고 있다.
따라서 이 코드는 s[i]에 어떤 값을 xor해서 ptr에 저장한다.

output은 결과이기에 2자리 Hex값 일 것이다.

그렇기에 output을 ptr에 넣고 다시 XOR 해주면 flag를 얻을 수 있을 것이다.
하지만 이 프로그램에서는 아까 말했듯, 출력을 2자리 Hex값으로 하기 때문에
따로 label의 XOR key값과 output의 문자열만 가지고 익스플로잇 코드를 작성 할 수 있었다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    const char *out_put = "220c6a33204455fb390074013c4156d704316528205156d70b217c14255b6ce10837651234464e";
    unsigned char key[8] = {0x66, 0x44, 0x11, 0x77, 0x55, 0x22, 0x33, 0x88};
    size_t len = strlen(out_put) / 2;
    unsigned char *flag = malloc(len);

    for (size_t i = 0; i < len; i++) {
        sscanf(&out_put[i * 2], "%2hhx", &flag[i]);
    }

    for (size_t i = 0; i < len; i++) {
        printf("%c", flag[i] ^ key[i % 8]);
    }

    putchar('\n');

    free(flag);
    return 0;
}

Recover


파일을 다운로드 받아보면 두 개의 파일이 존재한다.

그 중 소스 코드가 들어있는 파일은 chall이라는 파일이였다.
밑에 encrypted 파일은 무언가 암호화된 파일로 예상됬다.

이 소스를 분석해보면 읽기모드로 실행한 flag.png를 stream 에 저장한다.
stream 에서 1byte를 읽어서 ptr 에 저장하고, 4byte의 v6를 순환하여 XOR 연산을 수행한다.
XOR의 키 값은 v6이기 때문에 unk_2004에 들어있었다.

이 값에 19를 더하여 암호화하여 ptr 에 덧붙이고 쓰기모드로 실행한 encrypted 파일로 저장하는 방식이다.
디컴파일 코드의 복잡함에 비해 복호화 코드는 연산 부분만 역연산으로 수정 하면 되기에 쉽게 구연할 수 있었다.

#include <stdio.h>
#include <stdlib.h>

int main() {
    char ptr;
    int v5 = 0;
    unsigned char v6[4] = {0xDE, 0xAD, 0xBE, 0xEF};
    FILE *encrypted_file = fopen("encrypted", "rb");
    FILE *decrypted_file = fopen("decrypted.png", "wb");
    
    if (!encrypted_file) {
        puts("fopen() error for encrypted file");
        exit(1);
    }
    
    if (!decrypted_file) {
        puts("fopen() error for decrypted file");
        fclose(encrypted_file);
        exit(1);
    }
    
    while (fread(&ptr, 1, 1, encrypted_file) == 1) {
    ptr -= 0x13;
    ptr ^= v6[v5++ % 4];
    fwrite(&ptr, 1, 1, decrypted_file);
}
    
    fclose(encrypted_file);
    fclose(decrypted_file);
    return 0;
}

vsc로 실행하니

다음과 같은 png 파일이 생성되며 flag를 알 수 있었다.

개인적으로 fwrite 등의 파일을 여는 코드를 다뤄본 것이 오랜만이라서 복호화 코드만을 작성하면 된다는 것을 간과한 채, 뻘짓을 하여 시간을 다 날렸다 ...

profile
배고파요 ..

0개의 댓글