CTF write up(5)

yxxun1216·2023년 5월 24일

pwnable.kr


1. collision


문제에 MD5 hash collision을 사용하라고 나와있어서 이에 대해 알아보았다.

MD5는 128비트 암호화 해시 함수이다. 임의의 길이의 메시지를 입력받아 128비트짜리 고정 길이의 출력값을 낸다. 알고리즘은 사진으로 대체하고 설명은 생략하였다.

(출처 - 위키백과)

이 MD5는 암호화 결함이 발견되어 더 이상 암호화에 사용되지 않는다. 서로 다른 값을 넣었는데도 해시값이 똑같이 나오는 충돌이 발생하기 때문이다.

문제 서버에 접속했을 때 col, col.c, flag 파일이 있어서 col.c 파일을 열어보았다. 코드는 다음과 같다.

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
        int* ip = (int*)p;
        int i;
        int res=0;
        for(i=0; i<5; i++){
                res += ip[i];
        }
        return res;
}

int main(int argc, char* argv[]){
        if(argc<2){
                printf("usage : %s [passcode]\n", argv[0]);
                return 0;
        }
        if(strlen(argv[1]) != 20){
                printf("passcode length should be 20 bytes\n");
                return 0;
        }

        if(hashcode == check_password( argv[1] )){
                system("/bin/cat flag");
                return 0;
        }
        else
                printf("wrong passcode.\n");
        return 0;
}                                                           

메인 함수를 분석해보면 인자로 argc와 argv를 받는데, argv[1]은 20바이트여야 한다.

또한 hashcode와 argv[1]을 인자로 하여 실행한 check_password 함수의 실행값이 같을 때, flag를 확인할 수 있다.

이 때 입력한 argv[1]을 인자로 하여 check_password 함수가 실행되는데 check_password는 입력값을 다섯 번 더하는 실행을 한다. 따라서 원래 hashcode의 값을 5로 나누어 입력해야 한다.

0x21DD09EC를 5로 나누면 나누어 떨어지지 않기 때문에 0x6C5CEC8 * 4 + 0x6C5CECC‬로 입력해준다. (little endian 방법으로 입력해주어야 한다.)

daddy! I just managed to create a hash collision :) 플래그를 확인할 수 있다.

2. bof

bof 파일과 bof.c 파일을 다운받아 문제풀이를 해야 한다.

bof.c 파일을 먼저 확인했다.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
        char overflowme[32];
        printf("overflow me : ");
        gets(overflowme);       // smash me!
        if(key == 0xcafebabe){
                system("/bin/sh");
        }
        else{
                printf("Nah..\n");
        }
}
int main(int argc, char* argv[]){
        func(0xdeadbeef);
        return 0;
}

먼저 메인함수를 보면 인자를 전달받지만 func 함수에는 이미 정해진 인자값이 들어가 있다.

0xdeadbeef를 인자로 하여 func 함수를 실행시키는데, func 함수는 overflowerme라는 배열에 32바이트를 할당하고 이를 버퍼에 저장한다. 여기서 bof를 발생시키면 /bin/sh를 실행시킬 수 있다.

bof를 발생시키기 위해 0xdeadbeef와 overfloweme 사이의 거리가 얼마인지 알아본다.

확인해 보면 lea eax, [ebp-0x2c], DWORD PTR [ebp+0x8], 0xcafebabe에서 각각의 위치를 알 수 있고 총 52byte가 차이난다는 것을 확인할 수 있다.

따라서 52byte의 더미값과 4byte의 cafebabe를 입력해주면 된다.

daddy, I just pwned a buFFer :) flag를 확인할 수 있다.

3. flag

flag 파일만 있고 실행시켜봤더니 permission denied가 떴다.

chmod 명령어로 권한을 바꾸어 실행시켜 보았다.

I will malloc() and strcpy the flag there. take it. 이라는 문자열을 확인할 수 있다.

문제에 packed 된 파일이라고 나와있으므로 이를 unpacking 하기 위해 파일의 정보를 살펴본다. unpaking은 처음 해보는 것이라서 인터넷을 참고하였다.

ghex를 통해 알아보니 UPX라는 값이 보인다. 이는 압축 방법 중 하나로 이를 unpacking 해주면 파일을 분석할 수 있다.

아래 링크에서 upx를 설치했다.

https://github.com/upx/upx/releases

upx -d 명령어를 사용한다.

위와 같이 unpaking 된 것을 확인할 수 있다.

flag의 주소가 0x6c2070임을 알았다.

gdb의 x/xg 명령어는 메모리 주소에서부터 long 형식의 데이터를 16진수로 표시하라는 의미이다. 즉 x/xg 0x6c2070은 0x6c2070의 데이터를 64비트로 나타내라는 의미이다.

x/s 명령어는 메모리의 내용을 문자열로 나타내라는 의미이다.

따라서 gdb를 통해 UPX...? sounds like a delivery service :) flag를 확인할 수 있다.

0개의 댓글