[어설픈] 해커스쿨 FTZ Level 17 문제풀이

2019. 3. 2. 11:27System Hacking/FTZ


안녕하세요!

오늘은 오랜만에 

FTZ level 17번 문제를

풀어보도록 할게요!


제가 이 문제를 

풀고 이해하는데 좀 오래 걸려서..


어찌 되었건, 문제를 보면

아래와 같습니다!


전 문제 16번은 shell함수가 있어서

함수 포인터로 


"야 shell 함수 실행시켜"


이렇게 shell 함수 주소만

call 함수 포인터에 잘 넣어주면

바로 문제가 풀렸죠?


이번에는 바뀌었습니다!

shell 함수가 없어졌어요....


그러면 어떻게 해야하나?

저희가 직접 system함수 주소 넣고

/bin/sh 인자도 줘야죠!

네 맞아요 이번에도 RTL로 풀꺼에요!




main함수 코드보면 16번과 비슷하죠

(1) 스택에  crap 변수 쌓이고


(2) 그 위에 call 함수 포인터 쌓이고


(3) 그 위에 버퍼[20]만큼 쌓이고


(4) fgets 함수로 48만큼 입력 받아서 버퍼에 넣죠?

근데 버퍼는 20밖에 공간이 없으니 

오버플로우 발생하겠죠?


(5) 뭐 setuid 함수로 권한 레벨 18로 올리고


(6) call 함수 포인터에 있는 함수 call!


일단 어떻게 풀지 고민하기 전에 attackme 파일

tmp 폴더에 복사 할게요!


기본적인 세팅해야 하잖아요!

gdb로 attackme 그냥 돌리면 

attackme 파일 소유주가 level18이라 

저는 level17이라..


tmp 폴더로 이동해서 

pwd 명령어로 현재 위치 알아내고!

cp [복사할 파일]  [복사한 파일 가져올 위치]



그리고 ls -al 명령어로

attackme 파일 확인하면

이제 level17 소유죠?


자 이제 해결 방법을 생각해봅시다! 



소스코드를 보면 RTL을 두 곳중에 

한 곳을 골라서

공격 할 수 있어요!


(1) call 함수 포인터에!

or

(2) main 함수 끝나는 지점에 (우리가 자주 하던거)


(1)은 call 함수 포인터가 printit함수가 아닌

 system함수를 불러오게 해서

저희가 /bin/sh 인자를 넣어주면 

exploit 완료


(2)은 main함수의 return address를 조작해서

system 함수를 불러오게 해서

마찬가지로 /bin/sh 인자를 넣어주면

exploit 완료!

( main 함수가 정상적으로 돌아가야 하므로, 오버플로우 할 때 call 함수 포인터 printit으로 맞춰줘야함!!!)

 

근데 제가 여기서 시간이 오래 걸린 이유는 

(1)의 방식으로 풀려 시도하다가 안돼서...

근데 이제는 이해가 갔습니다!


그 이유는 바로!

저희가 일반적으로 RTL 할 때

함수의 에필로그 부분에서 했잖아요!


쉽게 말하자면 함수의 지역변수, EBP다 

지운 상태에서 RET_ADDR만 남은상태요!


<RTL 할 때 스택 상황>

[Before]               [After]


지역변수 1              (지워짐)  

  지역변수 2     ---->   (지워짐)     

       EBP                    (지워짐)       

           RET_ADDR              RET_ADDR(얘만 남음)

 aaaa                      aaaa      

     /bin/sh                    /bin/sh        


근데 (1)방식으로 풀면

 지역변수1, 지역변수2 ,ebp같은 애들이 

안 지워진 상태에요!


왜냐고요?

저런 애들은 leave를 해야 

다 날아가거든요


밑에 gdb 창에서

0x080484e6 <main+62> leave 이 부분!!!!!

에서 다 없어져요


근데 저희의 call( )함수 호출 부분은

0x080484e4 <main+60>에 있죠?

즉, leave 하기 전에 있습니다!


그럼 어떻게 해야 하냐?

저희가 system함수를 함수 포인터로 호출할 때

스택 상황을 살펴봐야 합니다!


이게 원래의 스택 상황이에요!

call 함수 호출하기 직전에!


call 함수를 호출하면 

바로 printit의 RET ADDR이 buf[20]위에 쌓입니다!

printit 함수 끝나고 다시 main으로 돌아와야죠!


그리고 printit 함수의 프롤로그가 진행되면서

프롤로그에 있는 push ebp때문에

EBP가 printit의 RET ADDR 위에 올라갑니다!

근데 저희가 조작 하고픈

스택 상황은 아래와 같습니다!


(1) call 함수 포인터에 system함수 주소를 넣고!


(2) buf[20]에서 제일 앞 부분 4자리

buf[0]buf[1]buf[2]buf[3] 요놈들을 불러서

bin/sh의 주소 값을 넣습니다!  

그럼 버퍼는 16byte가 남겠죠?


여기서 하실 수 있는 질문!ㅇ

왜 최상단 4byte냐?

왜냐면 저희가 항상 RTL 할때


EBP

RET ADDR(aaaa로 주로 설정)

/bin/sh인자 주소 값


이렇게 했잖아요!

그거 맞춰준 거에요! 



자 이제 어떻게 풀지 다 정했으니

exploit 할 때 필요한 값 세팅만 해요!


gdb를 켜서 보니

변수를 위해서 0x38을 할당하네요

0x38=56byte


저희가 먼저 찾아야 하는 것은

system 함수의 주소를 넣어야 하는

call 함수 포인터의 위치가 어딘지!!


gdb를 자세히 보니 <main+57> 부분에서 %eax에

%ebp+0xfffffff0값을 넘기네요 


그리고 이 %eax값을 call 하는 걸 보니

%ebp+0xfffffff0에 

저희가 원하는 주소 값이 들어가 있겠군요


그래서 main+60부분에 breakpoint를 걸었습니다!


그리고 프로그램을 run!

실행시킨 뒤에 fgets에서 입력을 받죠?


근데 4byte(4개씩) 단위로 끊어서

알파벳 a부터 n까지 넣었어요!

이 중에 어떤 글자가 $ebp+0xfffffff0에 넘겨지는지 보려구요!


그래서 $ebp+0xfffffff0을 보니

0x6b6b6b6b가 있죠?

0x6b=k 입니다!


k직전까지 글자가 40개이니

41,42,43,44 번째가 k의 장소 


즉, call 함수 포인터는 41~44번째 입력 값에 

영향을 받는군요!


자 이제 이거는 전에 문제 풀 때와 마찬가지로!

attackme가 어떤 라이브러리 의존성을 갖는지

(어떤 라이브러리를 사용하는지)


/lib/tls/libc.so.6 사용하죠!

주소 값은(0x42000000)


그러면 이 /lib/tls/libc.so.6 라이브러리에서

system함수는 어디있을라나?


아래의 nm 명령어로 쭉 뽑아서 

grep으로 system만 찾으면

저기있네요! 0x4203f2c0!!!!


그리고 /lib/tls/libc.so.6 라이브러리에서

/bin/sh 문자열이 어디 있는지 찾아볼까?


strings 명령어로 -tx 위치까지 나타나게!

127ea4 /bin/sh! 

근데 여기에 0x42000000(/lib/tls/libc.so.6 위치 값)를 더해야해요!

저 라이브러리 안에서 위치니까요!


그래서 payload를 작성하면 

아래와 같습니다!


제일 먼저 0x42127ea4 넣고(/bin/sh)+a를 36개 넣으면 여기까지 총 40byte 완료!+ ret address에 시스템 함수 주소!0x4203f2c0


그래서 아래와 같이 작성을 다 마치고!

입력을 누르면 

exploit가 완료됩니다!


고생많으셨습니다!