2. 충돌 (collision) - Pwnable.kr 해설

2020. 3. 8. 19:51문제연습/Pwnable.kr (PWN)

반응형

 

티스토리 블로그는 PC 환경에 최적화되어 있습니다.

모바일 유저분들은 아래 네이버 블로그를 이용해 주세요.

 

2. 충돌 (collision)

네이버 블로그는 모바일 환경에 최적화되어 있습니다.PC 유저분들은 아래 티스토리 블로그를 이용해 주세...

blog.naver.com

 

문제 내용

Daddy told me about cool MD5 hash collision today.

I wanna do something like that too!

 

ssh col@pwnable.kr -p2222 (pw:guest)

 

 

들어가기 전

what is MD5 hash collision?

 

1. hash function(해시 함수)

해시 함수(hash function)는 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는함수입니다.

이러한 해시 함수의 특성 덕분에 전자서명, 무결성 검증, 난수 생성 등 다양한 부분에서 활용되고 있습니다.

 

2.hash collision (해시 충돌)

해시 충돌이란해시 함수가 서로 다른 두 개의 입력값에 대해 동일한 출력값을 내는 상황을 의미합니다.

임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 것이 해시 함수의 기능인데, 함수 설계상의 문제로 인해 서로 다른 데이터에 대하여 똑같은 해시 값을 출력하는 경우 해시 충돌이 일어났다고 합니다.

 

MD5도 해시 함수의 한 종류인데 여러 구조적인 취약점으로 인하여 해시 충돌을 일으키는 알고리즘이 만들어졌다고 합니다.

 

MD5에 대한 정보를 알고 싶으시면 아래 링크를 이용해 주세요.

 

MD5 - 나무위키

이 저작물은 CC BY-NC-SA 2.0 KR에 따라 이용할 수 있습니다. (단, 라이선스가 명시된 일부 문서 및 삽화 제외) 기여하신 문서의 저작권은 각 기여자에게 있으며, 각 기여자는 기여하신 부분의 저작권을 갖습니다. 나무위키는 백과사전이 아니며 검증되지 않았거나, 편향적이거나, 잘못된 서술이 있을 수 있습니다. 나무위키는 위키위키입니다. 여러분이 직접 문서를 고칠 수 있으며, 다른 사람의 의견을 원할 경우 직접 토론을 발제할 수 있습니다.

namu.wiki

 

 

문제 해설

리눅스 터미널 화면을 열어서ssh col@pwnable.kr -p2222로 접속하고 pw인 guest를 입력하여 시작합니다.

​ ls 명령어를 이용해서 디렉터리 내용을 확인해보니 (터미널 창에 ls 입력 후 엔터)col col.c flag세 가지가 존재합니다.

​우선 확장자가. c인 col.c 파일을 읽어 봅시다. (터미널 창에 cat col.c를 입력 후 엔터)

 

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
#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;
}
 

음 소스코드가 저번 문제에 비해 좀 길어졌습니다.

천천히 살펴보도록 하겠습니다.

 

 

1
2
3
4
5
int main(int argc, char* argv[]){
    if(argc<2){
        printf("usage : %s [passcode]\n", argv[0]);
        return 0;
   }
 

main 함수의 인자의 개수가 2개 이하면 문자열을 출력해버리고 프로그램을 종료시켜 버립니다.

main 함수의 인자와 관련된 내용은 저번 시간의 fd 문제에서 상세히 다뤘습니다.

(자세한 설명은 아래 링크를 이용해 주세요.)

 

1. 파일 디스크립터(FD)

네이버 블로그는 모바일 환경에 최적화되어 있습니다.PC 유저분들은 아래 티스토리 블로그를 이용해 주세...

blog.naver.com

 

main 문에 전달되는 인자의 개수가 최소 2개 이상이어야겠군요.

다음 코드로 넘어가 보도록 하겠습니다.

 

 

1
2
3
4
if(strlen(argv[1]) != 20){
        printf("passcode length should be 20 bytes\n");
        return 0;
   }
 

main 문에 전달된 2번째 인자인 argv[1]의 길이를 비교하여 20byte가 아니면 문자열을 출력하고 프로그램을 종료해 버립니다.

여기서 제가 착각한 점이 main 문의 인자인 argv[]는 char*형이므로 어떤 값을 전달하는 4byte로 처리한다는 포인터 내용을 착각했습니다.

 

여기서 정리해야 될 점은 char*는 배열의 시작 주소만 가지기에 char*의 크기는 4byte가 되지만, strlen 함수는 문자열의 길이를 반환합니다.

보다 직관적인 이해를 위해 아래 소스코드와 링크를 올립니다.

(이번 기회에 포인터, 배열을 제대로 정리해 두세요!)

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>
 
int main() {
    const char *aasdf[5= { "asdf1111","ICMP","asdflkjh"};
 
    printf("%d\n"sizeof(aasdf)); //20을 출력 (4byte * 5)
    printf("%d\n"sizeof(aasdf[0])); //4를 출력
    printf("%d\n", strlen(aasdf[0])); //8을 출력
    
}
 
 

C 언어 코딩 도장: 41.1 문자열 길이 구하기

41 문자열의 길이를 구하고 비교하기 이번에는 문자열의 길이를 구하는 방법, 두 문자열이 같은지 비교하는 방법을 알아보겠습니다. 41.1 문자열 길이 구하기 문자열은 문자가 여러 개 모여있으므로 길이가 있습니다. 문자열의 길이는 strlen 함수로 구할 수 있으며 함수 이름은 문자열 길이(string length)에서 따왔습니다(string.h 헤더 파일에 선언되어 있습니다). strlen(문자열포인터); strlen(문자배열); size_t strlen

dojang.io

 

 

[C언어] 문자열과 char 포인터, 문자열을 다룰때 하는 실수들

문자열과 char 포인터 오늘은 심심한데 문자열에 대해서 이야기 해볼까 해요~ 문자열과 포인터는 C언어에서 너무나 귀찮은 놈들인데,,, 그래도 꼭 쓰이니까요 char 자료형은 문자를 변수로 갖는 건 모두 아는 사실..

reakwon.tistory.com

문자열 설명을 하니 이야기가 길어졌습니다.

다음 포인트로 넘어가 보겠습니다.

 

 

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

if 문에 main 문의 인자 argv[1]의 문자열을 check_password 함수에 넣어 나온 리턴 값이 hashcode와 같으면 flag를 출력하는 것 같습니다.

그럼 check_password 함수 원형을 확인해 보겠습니다.

 

1
2
3
4
5
6
7
8
9
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;
}
 

여기서부터는 검증되지 않는 내용입니다. 정확한 내용을 아시는 분들은 댓글로 피드백 부탁드립니다.

 

앞서 char* 배열은 문자열 시작 주소를 저장하기에 모든 배열의 크기가 4byte입니다.

그러나 char*는 데이터를 1byte밖에 못 읽지만 int*는 4byte를 읽을 수 있습니다.

아래 예시를 확인하면 이해하기가 훨씬 수월할 것입니다.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <string.h>
 
int main() {
    int i = 0x1234;
    int* p = &i;
    char* q = (char*)p;
    printf("%x %x %d"*p, *q,sizeof(*q));
    //각각 1234 34 1을 출력
}
 

여기서 *q가 34로 출력되는 이유는 컴퓨터가 자료를 리틀 엔디언 방식으로 저장하기 때문입니다.

리틀 엔디언 방식, 포인터의 형 변환에 대한 내용은 아래 링크를 이용하시면 보다 상세히 알 수 있습니다.

 

리틀 엔디안 VS 빅 엔디안

먼저 둘을 비교하기에 앞서 엔디언이란 무엇인가? 엔디언(Endianness)은 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하며, 바이트를 배열하는 방법을 특히 바이트 순서(By..

genesis8.tistory.com

 

[C언어] 포인터(2) - 포인터의 형변환

안녕하세요 ! 초보개발자 입니다. 이 블로그는 개인 공부 정리용 블로그 입니다. 혹 잘못된 내용이 있다면 지적 부탁드리겠습니다. 그리고 질문주신다면 최대한 아는선에서 답변드리도록 하겠습니다. 그럼 시작하..

kangchobo.tistory.com

 

 

C 언어 코딩 도장: 58.2 포인터 변환하기

이번에는 포인터끼리 변환하는 방법입니다. 이때는 자료형 뒤에 포인터를 나타내는 *(애스터리스크)를 붙여주고 괄호로 묶어주면 됩니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요. type_conversion_pointer.c #include #include // malloc, free 함수가 선언된 헤더 파일 int main() { int *numPtr = malloc(sizeof(int)); // 4바이트만큼 메모리 할당 char *cPtr

dojang.io

 

 

다시 본론으로 돌아오겠습니다.

1
2
3
4
5
6
7
8
9
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* ip = (int*) p;는 포인터 형 변환을 하였으므로 char* p에 들어있는 값을 5등분 하겠다는 말입니다.

char* 1byte 4개를 int*로 변환하니까 위에서 언급한 argv가 조건문을 만족한다는 가정을 하면 20byte,

형 변환을 통해 20byte / 4byte = 5입니다.

이후 반복문을 통해서 5등분 한 값을 모두 더한 후 res로 반환합니다.

 

 

-만약 여기까지 보고 방법이 떠오르셨다면 아래 내용을 보지 말고 한 번 더 다시 풀어보세요!!!

그럼collision문제의 공략을 세워보도록 하겠습니다.

 

1.argv[1]에0x21DD09EC의 값을 임의로 5등분 나누어 넣어준다.

(어떻게든 5등분의 합이0x21DD09EC이기만 하면 됩니다. 단 전달되는 인자의 크기가 20byte 여야 합니다.)

 

0x21DD09EC = 568134124 = 0x06C5CEC8‬ * 4 + 0x06C5CECC

그러면 이 데이터를 파이썬에 리틀 엔디언 방식으로 넣어줍시다!!!

 

터미널 창에 다음과 같이 입력해 줍시다.

./col `python -c 'print "\xc8\xce\xc5\x06"*4 + "\xcc\xce\xc5\x06"'`

데이터를 16진수 리틀엔디언으로 넣어야 하기에 구분을 위한 메타 문자 \x와 순서를 지켜서 넣으면 됩니다.

 

python의 옵션인 -c, 메타 문자 \x에 대한 내용은 아래 링크를 참고해 주세요.

 

2. 파이썬 인터프리터 사용하기 — Python 3.8.2 문서

2.2.1. 소스 코드 인코딩 기본적으로, 파이썬 소스 파일들은 UTF-8으로 인코드 된 것으로 취급됩니다. 이 인코딩에서는 대부분 언어에서 사용되는 문자들을 문자열 상수, 식별자, 주석 등에서 함께 사용할 수 있습니다. (하지만 표준 라이브러리는 오직 ASCII 문자만 식별자로 사용하고 있는데, 범용 코드에서는 이 관례를 따르는 것이 좋습니다.) 이 문자들을 모두 올바로 표시하기 위해서는 편집기가 파일이 UTF-8임을 인식해야 하고, 이 파일에 포함된

docs.python.org

 

 

정규 표현식(Regex) 강좌 4편. 메타 문자(meta character)

1. 메타 문자(meta character) 정규 표현식에서의 메타 문자(meta character)는 특별한 존재입니다. 메타 문자 그 자체는 특별한 녀석이며, 이미 사전에 약속되어진 문자를 뜻합니다. (특수한 문자나 다름이 없음..

blog.hexabrain.net

 

 

터미널에 입력 후 flag가 출력된 모습입니다.

 

오늘 내용은 여기까지입니다.

다음 시간에는 collision을 풀어보도록 하겠습니다.

이상 ICMP였습니다!!

 

반응형