소스코드가 많이 깁니다.
아왜
shellout()을 보면 shell을 띄우는 것을 확인할 수 있습니다.
귀찮지만 main()을 분석해 보겠습니다.
명령어를 반복적으로 입력받습니다.
명령어 입력 횟수가 100을 넘기면 메시지가 뜹니다.
check의 값이 0xdeadbeef인 경우 shellout()을 실행합니다.
0xdeadbeef가 아닌 경우 else 내부의 긴 명령으로 갑니다.
else 내부를 살펴보겠습니다.
File descriptor array인 fds를 0으로 초기화 해줍니다.
FD_ZERO를 통해 fds를 stdin으로 설정 해줍니다.
FD_SET을 통해 stdin을 fds에 집어넣습니다.
fds의 descriptor들 중에서 변경된 descriptor가 있는 경우 if문 안으로 들어갑니다.
stdin의 file descriptor가 변경된 경우 if문 안으로 들어갑니다.
stdin의 descriptor number는 0입니다.
File descriptor들을 이용하는 위의 명령들은
명령어가 입력됐는지를 검사하기 위해 있는 명령들입니다.
크게 신경쓰지 않아도 됩니다.
read 명령을 통해 stdin에 입력받은 명령어를 1-byte 읽어 x에 저장합니다.
x의 값이 '\r', '\n'인 경우 경고음("\a")을 울리고 break 합니다.
x의 값이 0x08인 경우 count를 1 감소시키고 백스페이스("\b \b")를 출력합니다.
이도저도 아닌 경우 count가 가리키고 있는 string 값에 x를 집어넣고
count를 1 증가시킨 후 break 합니다.
이제 값 입력이 가능한 부분을 찾았습니다.
BOF를 통해 check를 0xdeadbeef로 변경하면 되겠습니다.
gdb로 attackme를 열어봅니다.
Stack을 256(0x100)만큼 확보합니다.
BOF로 공격할 대상인 string과 check의 위치를 확인해 보겠습니다.
check를 검사하는 부분을 살펴봅니다.
check의 위치는 0xffffff98 (%ebp)입니다.
0xffffff98은 10진수로 -104 입니다.
해당 명령이 실행될 때의 ebp 값은 0xbffffb28 이므로
check의 주소는 0xbffffb28 - 104 = 0xbffffac0 입니다.
다음으로는 string의 위치를 살펴보겠습니다.
string의 주소는 main() 초반에 stack을 확보하는 구간을 이용해 알아보도록 하겠습니다.
ABCD 순서대로 값을 넣어보고 stack을 뒤져보면 쉽게 위치 확인이 가능합니다.
0xbffffac4부터 string이 시작되는 것을 확인할 수 있습니다.
check의 주소는 0xbffffac0 이고
string의 시작 주소는 0xbffffac4 입니다.
string은 check가 있는곳 반대쪽으로 자랍니다.
Stack 구조
Low addr
check (4-byte) |
string[0] |
string[1]
|
string[2]
|
...
|
string[99] |
High addr
코드의 count가 100을 넘겼을 때 나오는 메시지 "뭐하는 짓이냐?"가 떠오릅니다.
count를 이용해 string에 값을 넣으므로
입력한 값이 0x08인 경우 count를 감소시키는 것을 활용해
count를 음수로 만들고 값을 집어넣으면 되겠습니다.
공격해봅니다.
level19의 shell이 떨어졌습니다.