jujuwon
시크릿주주
jujuwon
전체 방문자
오늘
어제
  • 분류 전체보기 (106)
    • 🔠 프로그래밍언어 (35)
      • ☕️ Java (19)
      • 🐠 Python (15)
      • 🍠 Kotlin (1)
    • 🔙 Backend (16)
      • 🌿 Springboot (12)
      • 🐳 Docker (1)
      • ☁️ AWS (3)
    • 💼 CS (12)
      • 📶 Network (12)
    • 🕹 알고리즘 (14)
      • 📑 스터디 (2)
      • 💁🏻‍♂️ 백준 (9)
      • 👨🏼‍🔬 프로그래머스 (3)
    • 📚 Book (8)
      • 🔎 오브젝트 (4)
      • 🧪 TDD (2)
      • 📜 논문 (2)
    • 🔐 보안 (7)
      • 👾 Pwnable (7)
    • 📝 회고 (4)
    • 🧩 etc. (10)
      • ⚠️ issue (2)
      • 💡 꿀팁 (7)
      • ✏️ 끄적 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

최근 글

hELLO · Designed By 정상우.
jujuwon

시크릿주주

Pwnable.kr :: Toddler's Bottle - collision write up
🔐 보안/👾 Pwnable

Pwnable.kr :: Toddler's Bottle - collision write up

2021. 4. 12. 11:59
반응형

Pwnable.kr 의 두번째 문제를 풀어보자.

이번 문제는 MD5 hash collision 에 대한 문제라는 힌트를 주고 있다.

그러면 Hash 는 무엇이고 Hash collision 이 무엇인지 간단하게 알아보자.

Hash 란 임의의 길이를 갖는 임의의 데이터에 대해 고정된 길이의 데이터로 매핑하는 것을 의미한다.

한 개의 입력값에 대해 한 개의 출력값이 나오는데, 이 입력값의 범위는 무한한 데 비해

출력값의 범위는 유한하기 때문에 입력이 다름에도 불구하고 드물게 동일한 값이 출력되는 경우가 존재한다.

이러한 경우를 '충돌' (Collsiion)이 발생했다고 한다.

 

이제 문제를 살펴보자.

 

 

역시나 flag 파일이 존재하고 권한이 없기 때문에 setUID 가 걸려있는 col 파일을 이용해서 문제를 해결해야겠다.

그럼 소스 코드를 한 번 확인해보자.

 

 

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

hashcode 변수에 특정값으로 초기화를 하고, check_password 함수를 정의했다.

매개변수로 받은 데이터를 int 형으로 형 변환한 뒤, 반복문을 통해 4바이트씩 끊어서 (int 형 포인터이기 때문)

res 변수에 더한다.

 

main 함수를 살펴보자.

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

인자로 넣은 데이터의 길이가 20 byte 가 아니라면 역시 메시지를 출력하고 종료한다.

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

인자로 넣은 값을 check_password 함수로 전달해서 return 된 값을 hashcode 와 비교한다.

동일하다면 flag 파일을 출력하는 것을 볼 수 있다.

위의 조건문에 해당하는 것이 없다면 잘못된 passcode 라며 메세지를 출력하고 종료한다.

 

코드를 살펴봤으니 이제 해결방법을 찾아보자.

먼저 고민해야 할 것은 check_password 함수를 거친 이후 return 된 값이 어떻게 하면 hashcode 변수의 값과

동일하게 만들것인가이다.

check_password 함수를 보면 인자로 삽입한 값을 int 형으로 변환한 뒤 4바이트씩 끊어서 5번 더하고 있다.

그리고 main 함수에서 인자로 삽입한 값의 길이를 20 byte 로 맞춰야 한다는 것을 알려주고 있기 때문에

우리는 20 byte 의 데이터를 4바이트씩 나눠서 삽입해야 한다.

 

hashcode 에 저장된 값은 16진수로 0x21DD09EC 이다.

이것을 5로 나누면 (5번 더해서 hashcode 와 비교하기 때문에) 몫이 0x6C5CEC8 이고 나머지가 4이다.

0x6C5CEC8 를 4번 더하고 0x06C5CECC 를 1번 더하면 문제를 해결할 수 있다.

 

이것을 인자로 삽입하기 위해서 파이썬 스크립트를 사용하자.

쉘에서 python -c 를 이용하면 한 줄 스크립트를 바로 실행시킬 수 있다.

 

그 전에 'Endian' 이라는 개념을 알고 있어야 하는데,

Endian 이라는 것은 컴퓨터에 데이터가 저장되는 순서를 말한다.

Byte 단위로 데이터를 저장할 때 저장하는 순서(Byte oreder)가 아키텍처에 따라 다르다.

 

출쳐 : ko.wikipedia.org/wiki/엔디안

문제를 제공하고 있는 서버의 엔디안을 확인하는 명령어는 다음과 같다.

 

Little Endian 방식은 데이터를 저장할 때 작은 단위의 바이트, 즉 하위 바이트가 앞에 오는 방식이다.

그렇기 때문에 Byte 단위로 데이터를 삽입할 때 하위 바이트부터 삽입해야한다.

 

그렇다면 이제 python -c 를 이용해 스크립트를 짜보자.

0x6C5CEC8 를 4번, 0x6C5CECC 를 Little Endian 방식으로 삽입하면

"\xC8\xCE\xC5\x06" * 4 + "\xCC\xCE\xC5\x06"

다음과 같이 되고, 이를 python -c 를 이용해 스크립트로 작성하면

`python -c 'print "\xC8\xCE\xC5\x06" * 4 + "\xCC\xCE\xC5\x06"'`

다음과 같다.

쉘에서 백쿼터는 감싼 내용을 명령어로 인식하게 하는 역할을 한다.

백쿼터 안의 내용을 따로 실행해서 결과를 받아오고, 받아온 결과가 ./col 뒤 자리로 들어가면서 인자로 전달되는 것이다.

 

이제 문제를 해결해보자.

 

 

 

 

clear

728x90
반응형
    '🔐 보안/👾 Pwnable' 카테고리의 다른 글
    • Pwnable.kr :: Toddler's Bottle - random write up
    • Pwnable.kr :: Toddler's Bottle - passcode write up
    • Pwnable.kr :: Toddler's Bottle - bof write up
    • Pwnable.kr :: Toddler's Bottle - fd write up
    jujuwon
    jujuwon

    티스토리툴바