CTF write up(5)

yxxun1216·2023년 5월 24일
0

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개의 댓글