1. 문제확인
Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?
문제를 대충 해석해보면,
엄마가 패스워드 넣는 로그인시스템을 만들어보라고 말했어~
내 초기 C코드는 에러없이 컴파일이 되었지!
뭐 워닝이 쫌있긴했는데 누가신경쓰겠냐? 라고 하고 있습니다.
우선 주어진 접속정보로 서버에 접속해서 passcode.c를 봐야겠네여
2. 문제 코드 분석
아래는 문제로 주어진 passcode.c에여
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; } |
2-1) main()
프로그램 실행에 필요한 인자는 특별히 없어보이네여?
가장 먼저, Toddler`s Secure Login System 1.0 beta. 라는 문자열을 출력한 후
welcome, login을 순차적으로 호출 하고
Now I can safely trust you that you have credential :) 출력해주면 프로그램이 끝나고있어여
2-2) welcome()
welcome함수에서는 이름을 입력받기위해 char 배열 100바이트짜리가 하나 선언이 되어 있어여
enter you name : 에 맞추어 이름을 입력하면,
Welcome [입력한 이름]! 을 출력해줍니다. 여기서 %100s로 100바이트만 받는 다는 점만 인지해두세여
2-3) login()
login 함수에서는 패스워드를 입력 받고 인증에 성공하면 플래그를 읽어주는 의도로 만들고 싶었던 것 같은 소스를 볼 수 있어여
꼼꼼하신 분이라면 바로 이상한 점을 아실 수 있겠져? scanf에서 입력을 받는 변수에 &가 빠져있어여 ㅠㅠ
그리고 리눅스에서는 제대로 동작 하지 않는 것으로 알고 있는뎅, passcode1을 입력 받은 다음 입력버퍼를 날려주는 fflush()를 호출하고 있어여
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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); } } |
3. 디버깅
3-1) main()
2-1) 에서 살펴봤듯이 별내용은 없어 보이네여 printf를 실행하기 위한 put을 각각 호출 하고, welcome과 login을 순차적으로 호출하네여?
움..참고로 이번에 알게된 건데 문제서버에서 peda를 사용하고 싶으면 gdb를 실행한 후 source /usr/share/peda/peda.py를 쳐주면 peda가 실행되여ㅠ 넘나 편한 것..
3-2) welcome()
여기서부턴 자세히 봐야 할 부분이 있어여
맨처음 함수 인트로에 해당하는 스택프레임 부분이 +0과 +1라인에서 이루어지고 있어여
바로 다음 +3라인에서는 소스에서 살펴봤던 login함수 내 지역변수인 name[100]을 위한 공간확보를 하고 있네여
0x88만큼 스택을 확보했으니까 dummy까지 합쳐서 136바이트를 확보한 것을 알 수 있져?
실행을 해서 확인을 해보도록 하져
welcome+9에 브레이크 포인트를 건 멈춘 모습이에여
ebp는 0xffb771e8, esp는 0xffb77160으로 (0xffb771e8 - 0xffb77160)0x88만큼 스택 공간이 확보되어있는 것을 확인해 볼 수 있어여
여기까지의 스택 상황을 한번 그려본다면 아래처럼 되겠져?
welcome+48에서 scanf을 호출해서 입력을 받고 welcome+68에서 print를 해주는 부분을 분석해서 정확한 더미부분과 name변수의 공간을
확인해 봅시당!
welcome+68에서 브레이크를 걸고 진행을 해보면,
0xffde9198에 입력한 값(저는 JSBach를 입력했어여)이 위치하고 있는 것을 알 수 있어여, 이 정보를 가지고 스택을 다시 그려보져
확보 된 0x88(136)사이즈가 이처럼 사용되고 있다는 것 까지 알아냈습니다. welcome에서 볼 수 있는 사항은 여기 까지구 login을 보면서
이 내용을 어떻게 써먹을 지! 확인을 해보면 되겠습니당
3-3) login()
login함수도 역시 스택프레임을 구성하는 것으로 시작하고있어여 그리고 passcode1과 passcode2를 위한 공간을 확보하고있져?
0x28만큼 확보하고 있네여 login+6에 브레이크를 잡고 자세히 한번 살펴봅시당
ebp가 0xffde9208이고, esp가 0xffde91e0기때문에 0x28(40바이트)만큼 공간이 확보된 것을 알 수 있습니다.
위와 같이 스택을 똑같이 그려보면?
요렇게 그려 볼 수 있겠져? 근데 잘보면 ebp가 welcome함수랑 똑같아여
welcome함수 콜 후에 바로 login함수를 콜 했기 때문에, 당연한 것일 수 있지만 이게 실마리입니다.
우선 0x28공간이 어떻게 구분되어지는지 확인을 해봐야겠져!
passcode1을 입력을 받는 login+34부분에 브레이크 포인트를 걸고 한번 봅시당
scanf에서 사용하는 첫 번째 인자인 %d가 스택 최상단에 있고, 바로 아래 passcode1가 있겠네여?
이 passcod1의 값을 어떻게 가져왔는지 scanf 호출 전(login+24, +27)을 보면 알 수 있어여
mov edx, DWORD PTR[ebp-0x10] // edx에 ebp-0x10의 주소에 있는 값을 대입
mov DWORD PTR [esp+0x4], edx // esp+0x4의 주소에 edx값을 대입
즉, ebp-0x10 -> 0xffde9208 - 0x10 = 0xffde91f8에 있는 값을 가져다가 쓴다는 게 되는거져
아까 welcome에서 봤던 스택 모습을 한번 더 볼까여?
ebp는 welcome과 login이 동일하게 0xffde9208을 쓰고있습니다.
그래서 ebp-0x10 의 위치라면! ebp로부터 16바이트가 떨어진 곳의 값이 되겠네여, 그럼...
name입력에서 끝 4바이트가 된다는 것을 알 수 있습니다.
요기까지 상황을 정리해보겠습니당!
1. scanf에는 메모리에 쓰고싶은 곳의 포인터를 넘겨준다.
2. 그런데 개발자가 실수로 & 기호를 빼먹어서 변수에 있는 값을 포인터로 사용하게되었다.
3. 그 변수에 있는 값은 앞의 함수에서 입력한 값의 영향을 받는다.
이름을 입력할 때 마지막 네 바이트에 입력하고 싶은 메모리 번지를 넣고 그 다음 scanf에서 값을 쓰면
원하는 메모리번지에 값을 넣을 수 있다! 라는 결론 까지 올 수 있습니다.
한번 확인한 사항이 맞는지 테스트 값을 넣어서 확인을 해보겠습니다.
입력값은 A가 96개 B 4개를 넣었습니다. 우리가 확인 한 대로라면 scanf의 두 번째인자에는 0x42424242가 들어가 있어야되여...
확인한 대로 잘들어가있네여 ㅠㅠ
그런데 이걸 가지고 어떻게 익스플로잇 할 수 있을까여??
소스 상에서는 passcode1과 passcode2의 값이 모두 일치해야 조건을 만족하고 플래그를 읽어주는데...
결과부터 말씀드리면 passcod1을 입력받은 후 fflush함수를 호출 할 때 GOT의 주소를 바꿔줌으로써 플래그를 읽어볼 수 있습니다.
PLT / GOT가 무엇인지 자세히 알고 싶으신 분은 아래 링크를 참조 하세여!
PLT와 GOT 자세히 알기1
https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/
PLT와 GOT 자세히 알기2
https://bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/
[참조] https://bpsecblog.wordpress.com/
문제 풀이에 필요한 정도로만 간략히 설명을 드리자면
동적 라이브러리를 사용할 때 프로그램 내에 임포트한 라이브러리의 함수가 없기 때문에 (여기서는 fflush함수가 되겠습니다.)
해당 함수의 주소를 얻어 올 수 있는 테이블들입니다.
라이브러리 함수를 호출하게 되면 먼저 plt로 간 후, got로 점프를 뛰고 got에서 라이브러리의 함수 주소를 얻어오는 구조가 되겠습니다.
우리는 이 부분에서 fflush함수 호출 할 때, plt에서 got로 점프를 뛰는 부분의 주소를 login+127의 주소(system("/bin/cat flag"))로 바꾸어
익스플로잇을 시도해볼 수 있습니다.
그럼 수정해야 될 주소를 확인해 봅시당!
0x08048593 <+47>: call 0x8048430 <fflush@plt>
첫 줄에 바로 점프뛰는 부분을 보면 0x804a004로 점프를 뛰고 그 주소를 보니깐! got로 뛰어가고있는 모습을 볼 수 있네욥
여기서 우리가 바꿀 주소가! 바로 0x804a004에요, 여기다가 login+127의 주소인 0x080485e3을 넣어 줄꺼랍니다.
4. 페이로드 작성
작성해야될 페이로드를 프로토 타입으로 한번 보면 요렇게 될꺼에여
NOP 96개 + got로 뛰는 주소 + login+127의 주소
결과부터 보면
(python -c 'print "\x90"*96+"\x04\xa0\x04\x08"+"134514147"') | ./passcode 이렇게 되겠습니다.
이제 찬찬히 또 뜯어봅시당
프로그램에 입력을 넣어 주는 부분은 파이썬을 사용했어여
NOP에 해당하는 \x90이 96개 들어갔습니당, 그리고 리틀엔디언 방식으로 주소를 때려박아야하니깐 한바이트씩 역순으로!
0x804a004 -> \x04\xa0\x04\x08이 되는거고여
그리고 scanf에서 포맷이 %d 즉, 정수형으로 받고있기 때문에~
0x080485e3을 십진수로 변환해준 134514147을 넣어줬어여
근데 뭐 이상한거 없으신가여? 제가 초보자여서 그런지 여기서 헤맸어여 ㅠㅠ
name[100]에 100바이트가 넘는 문자열을 입력하면 오버플로우가 안나나? 하는 생각을 했더랬져
제가 혹시 위에서 얘기했던 %100s 기억나시나요? 저렇게 사이즈를 정해줌으로써 딱 100바이트만 입력받고 100바이트이상 넘치는 문자열은
다음 인풋으로 넘어가더라고여..ㅎ;;;
그래서 정리해보면~
처음 이름을 입력하는 곳에 NOP + 0x804a004까지 들어가고 그 다음 passcode1을 입력하려고했던 로직에
0x080485e3이 입력이 되서 플래그를 읽을 수 있었습니다~
'Study > Pwnable' 카테고리의 다른 글
[HITCON Training] lab4 / return to library (0) | 2017.11.29 |
---|---|
[HITCON Training] lab2 / shellcraft (0) | 2017.11.28 |
NX(No eXcutable) / ROP (1) | 2017.09.29 |
pwnable.kr / input / pwntools (0) | 2017.08.01 |
PEDA / pwnable.kr bof문제 (0) | 2017.07.12 |