처음써보는 pwnable.kr 라이트업 :)
[1] 코드분석
코드를 분석해보자. 일단 이 코드는 최대 100바이트까지의 이름을 입력받고, int형 패스워드 1,2를 입력받는다.
그 후 패스워드1과 2를 비교하여, 조건에 맞으면 /bin/cat flag로 플래그를 보여준다.
그래서 일단 맘편하게 바로 패스워드를 입력해보았는데 오류가 뜨는것을 볼수있다.
띠용! 왜일까?
일단 구글링해보니 fflush함수는 리눅스내에서 제 기능을 하지 못한다고 한다.
그래서 오류가 났을 상황에서는 passcode1 = 1234 / passcode2 = \n 이 들어있을 것이다.
그래서 우리는 일단 passcode2는 이용할수없고, 이름을 입력받는 name과 passcode1만을 이용해서 플래그를 찾아야한다.
너무 막막하다..
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 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <stdio.h> #include <stdlib.h> void login(){ int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2); printf("checking...\n"); if(passcode1==338150 && passcode2==13371337){ printf("Login OK!\n"); system("/bin/cat flag"); } else{ printf("Login Failed!\n"); exit(0); } } void welcome(){ char name[100]; printf("enter you name : "); scanf("%100s", name); printf("Welcome %s!\n", name); } int main(){ printf("Toddler's Secure Login System 1.0 beta.\n"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; } | cs |
다시 처음부터 코드를 쭉 살펴보자.
문제점은 바로 printf("enter passcode1 : ");
자, 일단 scanf에 오류가 있다는것을 기억한 상태로 gdb로 파일을 뜯어보자.
[2] GDB로 뜯어보기
일단 위의 welcome 함수부터 차근차근 분석해보자.
일단 welcome 함수에서 가장 중요한부분은 welcome+38 ~ +48 부분이다.
lea edx, ebp-0x70 으로 edx에 ebp-0x70 이라는 주소값을 넣어주었고, 이값이 mov되면서 scanf함수에 그대로 전달된다.
이를통해서 우리는 ebp-0x70이 name[100]의 시작주소라는것을 유추할수있다.
자, 그리고 login 함수로 가보자.
login 함수에서 우리가 주의깊게 봐야할 부분은 당연히 passcode1의 흐름이다.
우리가 사용할수있는 부분은 name[100]과 passcode1뿐이기 때문이다.
일단 welcome부분에서 name의 시작주소를 알아냈으니 이제 passcode1의 시작주소를 찾아보고, 값을 어떻게 써야할지 찾아보자.
login함수에서의 첫번째 scanf문은 login+34 부분이다. 그리고 여기서 조금만 위로 가보면
login+24 부분에서 *(ebp-0x10)을 edx에 복사해주고, 이 값을 아랫부분에서 계속 다루면서 내려가는것을 볼수있다.
이부분에서 우리는 *(ebp-0x10)이 passcode1의 시작주소라는것을 유추할수있다.
( 추가로, 두번째 scanf문은 login+80 부분이다. 위와 같은방법으로 passcode2의 시작주소를 찾으면 ebp-0xc가 시작주소라는것을 알수있다.
정리해보면, name[100] = 0x70 부터 100byte
passcode = 0x10 부터 4byte 를 입력받는다.
이 상황을 그림으로 그려보자.
열심히 그린 그림의 화질이 심상치않다. 슬프다.
위의 그림에서 색칠해둔 ??? 부분인 name에서 100바이트중 4바이트가 passcode1을 덮어쓸수있다.
그럼 여기 4바이트에는 무엇을 넣어야 할까?
[3] 페이로드 작성
*PLT, GOT를 이해하기위해서는 https://sunrinjuntae.tistory.com/26 여기로!
name이 passcode1을 4바이트만큼 침범하므로, 여기다가 값을 넣어주면 위에서 설명했던 scanf의 오류로 인해서 4바이트 덮어씌운 값이 passode1의 값이 될것이다.
자, 여기까지 페이로드를 작성해보자.
[dummy 96] + [ ??? 4]
그리고 여기서 우리는 scanf와 scanf사이에 있는 fflush함수의 GOT를 ??? 에 입력할것이다.
[dummy 96] + [ fflush@got 4]
그리고 passcode2를 입력받는 두번째 scanf에서 system함수의 시작주소를 입력하면 위의 코드처럼 "/bin/cat flag" 가 실행될것이다.
[dummy 96] + [ fflush@got 4] + [ &system 4] = exploit !!!!!
이제 우리가 필요한 주소들을 찾아보자.
먼저 fflush의 got이다.
노랑색으로 칠한부분이 fflush의 got이고...
노랑색칠한부분에서 인자("/bin/cat flag) 를 mov해주고 system함수를 call 하는것이니 노랑색으로 칠한 부분을 주소로 넣어준다!
[dummy 96] + [ 0x0804a004 ] + [ 0x080485e3] = exploit !!!!!
여기서 주의해야할점은, scanf로 입력받는 형식이 10진수 ( %d ) 이므로, 16진수값을 10진수로 변환해준다.
진짜로 다알아냈다. 이제 공격해보자.
우린해냈다.
(진짜개어렵다.)