Pwnable.kr - 담합: 작성

13270 단어 ctfsecurity
이것은 디버깅을 연습하고 C 코드를 읽는 데 좋은 재미있는 도전입니다(사실 잘 모르겠습니다).

공모는 인코딩된 암호와 동일한 서명을 가진 메시지를 제공하는 것입니다. 암호가 해싱 기능 X를 사용하여 데이터베이스에 인코딩된 방식으로 저장되는 것처럼 서비스에 로그인할 때 X에 해싱된 암호를 보내 데이터베이스에서 확인합니다. X와 같은 해시 Y를 직접 제공하면 암호를 찾지 않고 로그인할 수 있습니다. 예를 들어, MD5는 이에 취약합니다(다른 암호도 마찬가지). 해시 계산의 한계 때문입니다(제 생각에는).

따라서 주요 코드는 다음과 같습니다.

#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];
        printf(res);
    }
    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;
}


여기에서 몇 가지 흥미로운 섹션이 있습니다.

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];
        printf(res);
    }
    return res;
}


이 함수는 문자(즉, 문자열)를 얻은 다음 벡터(즉, 배열)로 캐스트합니다. int* ip = (int*)p; 섹션입니다. 여기 작업의 핵심이기 때문에 확실히 해야 합니다read more.

그런 다음 루프는 벡터를 반복하고 ip의 모든 값을 가져와 합산하여 결과를 반환합니다.

그 후 main() 함수는 다음과 같습니다.

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


3단계로 설명할 수 있습니다.
  • 명령줄 호출에 대한 인수가 하나만 있는지 확인하십시오
  • .
  • 인수는 20바이트 길이여야 합니다.
  • 주어진 암호가 hashcode와 같으면 챌린지를 pwn했습니다.

  • 여기서부터 무엇을 할까요?


    0x21DD09EC가 첫 번째 단서입니다. 우리는 이것으로 많은 것을 할 수 없습니다. 이것을 십진수로 변환합시다(나중에 이해가 됩니다).

    Python 3.9.1 (default, Dec 13 2020, 11:55:53)  [GCC 10.2.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 0x21DD09EC
    568134124
    >>> 
    


    그런 다음 check_password가 두 번째 단계입니다.

    루프는 5번 수행되고 결과를 반환합니다. 이것은 우리가 함수를 제공하는 것이 무엇이든 5배로 추가된다는 것을 의미합니다. 따라서:

    568134124 = 5x
    


    그 다음에:

    568134124 / 5 = x
    


    하지만!

    >>> 568134124 / 5
    113626824.8
    


    여기서 부동 소수점이 아닌 정수로 작업해야 합니다. 그런 다음 방정식을 수정하여 나머지 또는 함수를 입력해 보겠습니다.

    568134124 = 4x + x'
    


    (아마 더 수학적인 방법이 있을 것입니다. 댓글로 알려주세요. 저는 꽤 오래 전에 학교를 떠났습니다)

    >>> 568134124 // 5
    113626824
    


    그 다음에:

    568134124 = 4 * 113626824 + x'
    568134124 - (4 * 113626824) = x'
    113626828 = x'
    


    테스트:

    >>> 568134124 == (4 * 113626824) + 113626828
    True
    


    문제 없다. 익스플로잇을 작성해 봅시다. 이를 위해 pwntools 을 사용할 것입니다.

    두 개의 소수 섹션은 리틀 엔디안이고 32비트 아키텍처의 경우 다음과 같습니다.

    p32(113626824, endian='little')
    # and
    p32(113626828, endian='little')
    


    다음은 익스플로잇입니다.

    from pwn import *
    
    payload = p32(113626824, endian='little') * 4 + p32(113626828, endian='little')
    p = process(['./col', payload])
    ret = p.recvline()
    print(ret)
    


    프로세스를 호출하기 전에 페이로드가 필요한 이유는 무엇입니까? 페이로드를 명령줄 인수로 전달해야 하기 때문입니다.

    결과뿐만 아니라 전체 계산을 하는 이유는 무엇입니까? 20바이트 페이로드가 필요하기 때문입니다. 우리는 결과뿐만 아니라 지시를 전달합니다. 방법과 이유에 대한 자세한 내용은 나에게 약간 모호합니다. 나는 몇 가지를 시도했고 이것은 결국 효과가있었습니다.

    ~/.../pwnable.kr/2 >>> python boum.py                                                                                        
    [+] Starting local process './col': pid 99792
    b'/bin/cat: flag: No such file or directory\n'
    


    그리고 압니다! 당신이 그것을 즐겼기를 바랍니다!

    좋은 웹페이지 즐겨찾기