Pwnablekr_Toddlers Bottle_collision

C 파일 코드이다.

핵심 알고리즘을 정리하면 다음과 같다.

  • argv[1]의 길이가 20인지 체크한다.
  • check_password 함수의 결과가 전역변수 hashcode의 값(0x21DD09EC)과 같은지 체크한다.
  • check_password 함수는 argv[1] 문자열의 아스키코드를 int 단위로 잘라서 더한다.
  • 문자열의 아스키코드를 int 단위로 잘라 더한 값이 0x21DD09EC와 같다면, flag를 읽는다.

이 조건에 맞추기 위해서는 우선 argv[1]의 길이를 20으로 맞춰주어야 한다.

그리고 문자열의 아스키코드를 int 단위로 잘라 더한 값이 0x21DD09EC를 만족해야 한다.

우선 gdb로 check_password 함수를 확인해보았다.

shl eax, 0x2 코드로 보아 int의 크기가 4바이트임이 명확해졌다.

이제 argv[1] 문자열의 아스키코드를 4바이트 단위로 읽어들일 때 생기는 일에 대해 생각해 보자. 메모리에서 값을 읽을 시 항상 리틀엔디안 빅엔디안에 주의해야 한다. 바이트 단위로 입력받은 문자열을 4바이트 단위로 읽을 시, 리틀 엔디안의 경우 바이트가 입력된 순서의 반대 순서로 뒤바뀐다.

서버에서 어떤 바이트 정렬을 이용하는지 알기 위해 CPU를 검색했다.

intel x86은 리틀 엔디안을 쓴다.

이제 위에서 알아낸 단서들을 종합하여 적절한 입력값을 넣어보자.

정상적인 문자로 입력하기 어려운 값을 입력하기 위해 파이썬을 이용했다. 바이트 정렬에 주의하여 값을 넣었다.

처음 16개의 바이트로는 0x01을 넣었다. 16개의 0x01을 4바이트 단위로 떼어내어 더한다면, 그 총 합은 0x04040404이다.

원하는 결과값은 0x21DD09EC이므로, 남은 4바이트는 그 차인 0x1DD905E8가 되어야 한다. 입력한 문자열을 4바이트 단위로 읽어들였을 때 0x1DD905E8가 되기를 원하므로, 리틀엔디안을 적용하여 0xE805D91D를 입력했다.

성공하고 flag를 출력해 준다. flag는 daddy! I just managed to create a hash collision :) 이다.


tags: writeup, pwnable, elf file, linux, c lang, hash collision, x86asm