-
[pwnable.kr] passcode 본문
0) 문제 개요
#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가지 존재한다. 첫번째는 login() 함수에서 scanf로 입력을 받을 때 해당 변수의 주소(&)가 아닌 변수 그 자체를 인자로 전달하는 것(C 처음 접하는 분들이 자주 하는 실수), 두번째는 stdin에 대하여 fflush를 하는 것. 참고로 fflush는 stdout에 대해서만 정의되어 있으며 입력 버퍼를 비우고 싶다면 보통 getchar()를 사용하게 된다.
- c-faq.com/stdio/stdinflush.html
Question 12.26a
c-faq.com
제시된 문제 해결하는 방법은 두 passcode에 올바른 값을 넣어서 if문을 통과하는 것인데, scanf를 저렇게 사용하였기 때문에 정상적인 방법으로는 해결할 수 없다. 다만 실마리가 보이는 것은 앞서 언급한 두가지 이상한 점에서 1) scanf에 주소(&)가 아닌 변수를 집어 넣었을 경우 해당 변수가 원래 가지고 있었던 값(쓰레기 값)을 주소로 인지하여 임의의 영역에 쓰기(memory write)가 가능하다는 것 하나와 2) 굳이 표준에 정의되지 않은 fflush(stdin)를 사용한 점(이렇게 사용했을 경우 프로그램에는 아무 영향도 없다)을 미루어 보았을 때 이 두가지를 이용하는 것이라고 추측해볼 수 있다.
덧붙여, 인과 관계가 살짝 어긋난 소리인거 같지만 풀으라고 준 문제이기 때문에 아마도 위에서 말한 실마리를 활용할 수 있도록 판을 짜놨을 것이라고 생각이 된다. 앞서 호출되는 함수 welcome()에서 어떠한 행위에 의해 passcode 변수를 조작할 수 있다면 내가 원하는 메모리 영역에 arbitary write이 가능할 것이고, 이를 활용하여 있으나 마나한 fflush의 got를 system("/bin/cat flag")의 주소로 overwrite하면 풀리지 않을까? 라는 포인트를 잡고 문제에 들어가보자.
1) passcode를 찾아보자
passcode1을 변조하기 위해서는 welcome()에서 뭔가를 해야한다고 말했는데, 막상 코드를 보니 함수에서는 100 바이트 입력만 받고 땡이다. 심지어 scanf에 포맷을 정해놔서 Buffer Overflow도 안되는 상황. 즉 정상적인 입력만으로 passcode1을 바꿀 수 있다는 것이다.
처음에 생각났던 아이디어는 name[100] 변수가 있던 공간에 passcode1이 할당 된다면? 이었다. 미리 말하자면 이 추측이 들어 맞았고, 이것이 문제를 해결하는 키 포인트 였다. 심지어 이 버그에 대해서는 이름도 있다. Use of Stack Memory After Function Return 이라고, 예전에 심심해서 찾아봤던 Apple의 Article에서 소개된 적이 있다.
마치 Use After Free Bug 처럼, 사용했던 공간을 제대로 초기화 하지 않아서 다른 프로시저에서 사용할 수 있게 된다는 버그이다. 이 문제에서는 사실 너무 억지스럽게 그려지긴 했지만 이 아이디어를 떠올리는데 이틀이나 걸렸다 ㅎ.. 그러면 name 변수에 더미를 마구마구 넣어서 실제로 passcode1이 그 공간을 다시 사용하는지, 사용한다면 몇 바이트부터 사용하는지 파악해보자.
100 바이트만큼 A를 넣어봤다.
login()함수의 첫번째 scanf가 호출되는 부분에 멈취서 passcode1(ebp-0x10) 쪽 스택을 살펴보니 아주 절묘하게 4바이트 만큼 A가 채워져 있는 것을 볼 수 있다. 즉, name[100]의 마지막 4바이트를 passcode1이 사용하고 있다는 소리.. 그럼 이제 passcode1에 fflush@got를 쓰고 -> 원하는 부분으로 점프만 하면 된다.
2) 마무리
이제, 페이로드를 짤 것인데 다음과 같이 구성할 것이다.
dummy[96] + fflush@got[4] + system("/bin/cat flag")[4]
이 때 system("/bin/cat flag")가 들어가는 부분(실제 scanf가 받아 들이는 값)은 포맷이 %d이므로 10진수로 바꾸어 작성할 것에 유의한다.
끗!
'Write-up > pwn' 카테고리의 다른 글
[pwnable.kr] md5 calculator (0) | 2020.08.05 |
---|---|
[TG:HACK 2020] Chunk Norris (0) | 2020.07.22 |
[pwnable.kr] bof (0) | 2020.07.22 |
[DawgCTF 2020] bof to the top (0) | 2020.07.22 |