문제 해석을 먼저해보자..
로그인 시스템을 기반으로한 passcode프로그램이다. 아무 에러없이 C코드가 컴파일이 되었다. 근데 컴파일에 경고가 있었다.
바로 접속후 디렉토리 확인.
C 코드를 확인해보자
아 길다 ㅆㅃ
앞에서 힌트로 주어진 "warning이 발생했다"를 한번 해석해보면 소스코드는 컴파일이 되었으나 경고가 떠 제대로 작동을 하지 않는다는 뜻이다. 소스코드를 쭉 보다보니 대부분의 scanf에 문제가 있는 것을 확인 할 수 있다.
이 부분을 길게 먼저 설명하겠다.
scanf는 char를 제외한 int형이나 그 등등 scanf("받을형식", 변수주소값)으로 사용된다.
char변수형인 경우에는 초기값과 주소값이 동일하기 때문에 &를 붙이지 않아도 되나.. int형은 &를 붙여주지 않으면 황당한 결과가 발생한다. 예를 들어 scanf("%d",a)라고 썻다면 a라는 주소에 값을 넣는다는 뜻이다. a를 a주소 자체로 생각해버리기 때문이다.
그 다음 문제점이다. login()함수 내에있는 fflush(stdin)부분!
이 부분 때문에 우리가 정상적으로 프로그램을 실행하고 올바르게 입력해도
세그먼테이지 폴트가 반환되는 문제가 발생한다.
저 함수로 인해 우리가 위와같이 올바른 패쓰워드를 입력했다해도 실제로 저장되어버리는 값은
passcode1=338150 / passcode2=\n 이다.
이런 일이 일어나는 이유는 소스상으로나 윈도우 상에서는 문제가 되질 않는다.
다만 리눅스에서는 조금 다르게 작동하고, fflush(stdin)은 공식적인(?) 표준이 아니다.
이 녀석은 리눅스에서는 아무런 기능(개행"\n"을 삭제)을 할수 없다.
이 점들을 숙지하고 다음으로 넘어가자
우리가 입력할 수 있는 버퍼를 계산하기 전에 어셈코드들을 확인해보자. 위 스샷은 welcome과 login사이에 별다른 입출력(PUSH/POP)이 일어나지 않는다.
이 곳은 첫번째 passcode1을 입력하는 부분이다.
이 부분을 어셈코드로 보면
상단의 스샷처럼 ebp-0x10 부분이다.
그다음 welcome()함수에서 처음으로 입력받는 부분인 name입력부분이다.
여기서는 name변수가 100만큼이다.
어셈코드로 보면 ebp-0x70부분에 name함수의 시작부분이 보인다.
최종적으로 우리가 더미로 채워야하는 부분을 계산하면 0x70~0x10사이가 되고 크기를 10진수로 변환하면
96만큼의 더미를 먼저채워야한다.
그리고 페이로드를 짜보자.
우리가 소스코드는 고칠수가 없으니, passcode1 입력 부분을 GOT(Global Offset Table)을 exit의 GOT로 바꿔서 우회해야한다.
exit의 GOT는 readelf -r passcode 명령어로 확인을 하면 아래 스샷과 같이 볼 수 있다
그리고 exit으로 빠져나올 뿐만아니라 system("/bin/cat flag")을 실행시켜야 하므로
system("/bin/cat flag")가 있는 주소를 찾아보면 아래 스샷과 같이 찾을 수 있다
0x08045e3주소에 있다는 것을 확인할 수 있다. 그리고 여기서는 주소가 아닌 정수를 넣어야 하므로 10진수로 변환하면
134514147이 나오는 것을 확인할 수 있다. 그리고 passcode2로 인해 세그먼테이지 폴트를 안띄우게 하기위해 문자 하나이상 입력한다
최종 페이로드는 파이프라인으로 작성하여서 (perl -e 'print "A"x96,"\x18\xa0\x04\x08","134514147","a\n"'; cat)| ./passcode
가 되고 키값이 나오는 것을 확인
추가적으로 이게 10pt짜리입니다.. 꽤나 어려워서 이해하는데 하루정도 걸린거같네요...
만약 누락된 설명이 있다면 추가보충할 예정