문제 코드 | 문제 파일
#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==123456 && passcode2==13371337) {
printf("Login OK!\n");
setregid(getegid(), getegid());
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.1 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}문제 분석
if(passcode1==123456 && passcode2==13371337)다음 조건문 코드를 만족하면 system함수를 통해서 flag를 추출할 수 있는 문제이다.
하지만 코드를 조금만 분석해 보면 scanf에서 &없이 데이터를 받아서 어떻게 입력하더라도 만족할 수 없는 상황입니다.
int main() {
printf("Toddler's Secure Login System 1.1 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}그럼 다른 방법을 찾아봐야 하는데, main함수에서 함수를 호출하는 순서를 보면 welcome 이후 login()함수를 실행하는 것을 볼 수 있었습니다.
이 경우에는 프로그램의 stack 영역의 특징인 메모리 영역을 재사용한다.
Stack 재사용 특성
int main() {
welcome();
login();
}
함수 호출 순서를 보면 welcome이후 return되고 login함수가 바로 실행됩니다. 이 경우 기존에 welcome에서 사용한 stack의 메모리 영역을 재사용하게 됩니다.
welcome() 호출 시:
┌──────────────────┐ ← 스택 상단 (낮은 주소)
│ name[100] │ ← 100바이트 버퍼
│ ... │
│ SFP / RET │
└──────────────────┘
welcome() 리턴 후:
┌──────────────────┐
│ (해제됨) │ ← 스택 포인터가 올라감
│ 하지만 값은 │ ← 데이터가 지워지지 않고 남아있음!
│ 그대로 남음 │
└──────────────────┘
login() 호출 시:
┌──────────────────┐ ← 스택 상단 (같은 주소 재사용!)
│ passcode1 │ ← welcome의 name 끝부분과 겹침
│ passcode2 │
│ ... │
└──────────────────┘스택의 경우 메모리가 리턴할 때 메모리를 초기화하지 않습니다. 또한 스택 포인터만 위로 올릴 뿐, 다음 함수가 같은 공간을 재사용합니다.
Tip
mitigation중 pie가 있을 경우 해당 취약점은 사용하기 힘들어집니다. 그 이유는 pie의 경우 주소를 계속 랜덤하게 바꾸므로 항상 동일한 stack 영역에 함수가 실행되는 것을 보장하지 않기 때문입니다.
취약점 분석
Arbitrary Write
int passcode1;
scanf("%d", passcode1);위 코드에서 int passcode1의 값을 초기화 하지 않았으므로 스택의 쓰레기 값이 그대로 저장됩니다. 여기서 scanf 함수가 passcode1의 값을 &없이 받았으므로 passcode1의 값을 주소 그대로 사용합니다.
GOT overwrite
welcome 함수의 scanf 영역 동적 분석 코드
ebp-0x70 영역에 입력 값을 scanf로 저장하고 있습니다. 단 코드에는 문자열의 길이는 100이라고 했지만 할당된 영역이 0x70 = 112라는 사실이 약간 의야하네요

exploit code
from pwn import *
payload = "A" * 96 + p32(0x80490a0)
배운 내용
fflush함수
C 언어의 입출력 성능 문제를 해결하기 위해 버퍼를 사용하는 함수이다. printf로 출력한 데이터가 바로 화면에 출력되지 않고, 메모리 버퍼에 쌓아놨다가, 가득 차거나 특정 조건이 될 때 한꺼번에 출력된다. fflush는 버퍼에 쌓여 있는 데이터를 즉시 강제로 내보내는 함수이다.
#include <stdio.h>
int fflush(FILE *stream)- 매개변수 : FILE * 타입의 스트림 포인터
- 특정 스트림을 넘기면 해당 스트림의 버퍼만 flush
- NULL을 넘기면 현재 열려 있는 모든 출력 스트림의 버퍼를 flush
- 반환값 : 성공 시 0, 실패 시 EOF