level 1에 접속하면 다음과 같이 나온다.
일단은 my-pass 라는 명령어가 있다 했으니 my-pass를 사용해보았다.
my-pass 명령어는 해당 유저의 비밀번호가 출력되는 명령어인 듯 하다. 그러니까 level2의 권한을 얻어 my-pass를 통해 비밀번호를 알아내고, level2로 로그인해서 level3의 권한을 얻어 my-pass를 통해 비밀번호를 알아내고 level3로 로그인하고 ...
이러한 방식인 듯 하다. 먼저 무엇부터 해야 할 지 모르겠으니 ls -al부터 출력해보았다.
음... 뭐가 굉장히 많이 보이는데 일단은 SetUID에 대해 배웠기 때문에 해당 디렉토리에서 이를 찾아보려했지만 s권한은 보이지 않는다. hint를 실행해보니 실행되지 않는다. 텍스트 파일인가 싶어 cat hint를 입력했다.
level1 디렉토리가 아닌 다른 어딘가에 열쇠가 될만한 파일이 숨어있는 것 같다. 혹시 level2의 권한을 가진 무언가가 존재하지 않을 까 싶어서 일단은 find / -user level2 를 이용해서 level2의 유저의 소유로 된 파일을 찾아보았다.
전부 다 허가 거부된 와중에 뜬금없이 /bin 에 level2의 파일인 ExecuteMe가?? 이름마저도 "나를 실행해라"라니.
bin 디렉토리는 명령어를 저장하는 곳이기 때문에 이 곳에 저장된 파일은 /bin/ExecuteMe 가 아닌 ExecuteMe 명령어만 입력해도 될 것 같아 ExecuteMe를 입력해보았다.
오....!! 오오......
뭔가 해답을 찾은 것 같긴 한데 어떤 명령을 써야 하나... my-pass를 통해 비밀번호를 출력할까 싶었지만 my-pass는 제외라니...
그래도 혹시 모르니 my-pass를 입력해보았다.
하지 말라는 게 진짜로 하지 말란 의미였나보다. 그렇다면 명령어를 딱 하나만 써서 level2로 넘어갈 수 있는 방법이 무엇이 있을까.... chmod도 안된다니 권한을 바꾸어 줄 수도 없을 것이다.
일단 ls -al /bin/ExecuteMe 를 통해 해당 실행파일의 권한과 소유자를 보았다.
level2 소유의 파일이며 level1 그룹의 유저가 사용할 수 있다. 일단 level1을 풀기 위해 존재하는 파일인 것에는 틀림없는 것 같다.
무엇을 쓸까 고민하면서 level1 디렉토리에서 ls -al만 주구장창 입력하다가 이상한 점이 보였다. level1 디렉토리 내에 pubilc_html 디렉토리와 tmp 디렉토리가 있는데, public_html 디렉토리에는 아무도 쓰기 권한을 가지지 않지만, tmp 디렉토리에는 그룹 내의 유저가 쓰기가 가능한 것이 보였다. 둘의 권한 설정이 다른게 이상하다고 생각하던 찰나, 만약 level2의 권한으로 my-pass 명령어가 아닌 my-pass 명령어를 실행하도록 하는 c 코드를 작성해서 해당 프로그램을 실행하면 어떨까 싶었다. 분명 버퍼 오버플로우에 대해 공부했을 때, c언어 코드에 system("/bin/bash") 같은 코드가 있었는데, 이러한 방식으로 system("my-pass");를 작성해서 level2의 권한으로 해당 프로그램을 실행시켜보고 싶어진 것이다. level1에서 쓰기가 가능한 곳은 tmp 디렉토리 뿐이기에 tmp 디렉토리에 test.c 라는 파일을 만들었다.
다시 ExecuteMe 를 실행시켜본다. 제발...
test는 level1/tmp에 있는데... level2 디렉토리에서 곧바로 실행하려니... 될 리가 없다.
경로를 어떻게 찾아갈까... 하다가 level1에서 상위 디렉토리로 가보았다.
모두 같은 디렉토리 내에 존재하고 있었다. 경로를 알았으니 다시 실행하러 가자.
../level1/tmp/test를 입력해보았다.
??????????????????????????????????????
진짜로 풀린건가...? 믿기지 않으니 level2로 로그인 해보기로 한다.
아니 왜 안되지...?
접속을 종료한 후에 level2로 로그인을 하고 패스워드를 다시 입력했더니 로그인이 됐다. 이렇게 푸는 게 맞나..? 싶지만..
모로 가도 서울로 가면 된다는 말이 괜히 있는가..!
아마도 어떤 명령어를 실행하시겠습니까? 에서 입력받을 때, my-pass, chmod가 입력되면 예외처리를 하는 무엇인가 있을거라 생각되는데, 어떠한 키워드로 접근해야 할지 감이 안잡히던 중
bin 디렉토리에 있는 명령어를 실행한다... 실행? 만약 ExecuteMe가 실행파일이라면 gdb를 통한 디버깅이 가능할까? 라는 의문이 들어 gdb /bin/ExecuteMe를 사용해보았다.
일단 실행에 실패했다는 이야기는 없는 것 같다. 무엇을 해야할까 생각하다 disas main을 쳐보기로 했다.
오오! 익숙한 단어들이 보인다. 중간중간 printf 함수를 호출하는 것으로 보아, ExecuteMe가 C로 작성된 파일인 듯 하다. 그렇다면 내가 원하는 건 명령어를 입력 받는 부분이기 때문에 좀 더 아래로 내려보았다. 문법부터 intel 방식으로 바꾸고...
ExecuteMe의 main 함수 전체의 어셈블리 코드는 다음과 같다.
(gdb) disas main
Dump of assembler code for function main:
0x08048488 <main+0>: push ebp
0x08048489 <main+1>: mov ebp,esp
0x0804848b <main+3>: sub esp,0x28
0x0804848e <main+6>: and esp,0xfffffff0
0x08048491 <main+9>: mov eax,0x0
0x08048496 <main+14>: sub esp,eax
0x08048498 <main+16>: sub esp,0xc
0x0804849b <main+19>: push 0x8048680
0x080484a0 <main+24>: call 0x8048358 <system>
0x080484a5 <main+29>: add esp,0x10
0x080484a8 <main+32>: sub esp,0xc
0x080484ab <main+35>: push 0x804868f
0x080484b0 <main+40>: call 0x8048378 <chdir>
0x080484b5 <main+45>: add esp,0x10
0x080484b8 <main+48>: sub esp,0xc
0x080484bb <main+51>: push 0x80486a0
0x080484c0 <main+56>: call 0x80483a8 <printf>
0x080484c5 <main+61>: add esp,0x10
0x080484c8 <main+64>: sub esp,0xc
0x080484cb <main+67>: push 0x80486e0
0x080484d0 <main+72>: call 0x80483a8 <printf>
0x080484d5 <main+77>: add esp,0x10
0x080484d8 <main+80>: sub esp,0xc
0x080484db <main+83>: push 0x8048720
0x080484e0 <main+88>: call 0x80483a8 <printf>
0x080484e5 <main+93>: add esp,0x10
0x080484e8 <main+96>: sub esp,0xc
0x080484eb <main+99>: push 0x8048760
0x080484f0 <main+104>: call 0x80483a8 <printf>
0x080484f5 <main+109>: add esp,0x10
0x080484f8 <main+112>: sub esp,0xc
0x080484fb <main+115>: push 0x8048782
0x08048500 <main+120>: call 0x80483a8 <printf>
0x08048505 <main+125>: add esp,0x10
0x08048508 <main+128>: sub esp,0x4
0x0804850b <main+131>: push ds:0x8049948
0x08048511 <main+137>: push 0x1e
0x08048513 <main+139>: lea eax,[ebp-40]
0x08048516 <main+142>: push eax
0x08048517 <main+143>: call 0x8048368 <fgets>
0x0804851c <main+148>: add esp,0x10
0x0804851f <main+151>: lea eax,[ebp-40]
0x08048522 <main+154>: sub esp,0x8
0x08048525 <main+157>: push 0x804879c
0x0804852a <main+162>: push eax
0x0804852b <main+163>: call 0x8048388 <strstr>
0x08048530 <main+168>: add esp,0x10
0x08048533 <main+171>: test eax,eax
0x08048535 <main+173>: je 0x8048551 <main+201>
0x08048537 <main+175>: sub esp,0xc
0x0804853a <main+178>: push 0x80487c0
0x0804853f <main+183>: call 0x80483a8 <printf>
0x08048544 <main+188>: add esp,0x10
0x08048547 <main+191>: sub esp,0xc
0x0804854a <main+194>: push 0x0
0x0804854c <main+196>: call 0x80483c8 <exit>
0x08048551 <main+201>: lea eax,[ebp-40]
0x08048554 <main+204>: sub esp,0x8
0x08048557 <main+207>: push 0x80487e8
0x0804855c <main+212>: push eax
0x0804855d <main+213>: call 0x8048388 <strstr>
0x08048562 <main+218>: add esp,0x10
0x08048565 <main+221>: test eax,eax
0x08048567 <main+223>: je 0x8048583 <main+251>
0x08048569 <main+225>: sub esp,0xc
0x0804856c <main+228>: push 0x8048800
0x08048571 <main+233>: call 0x80483a8 <printf>
0x08048576 <main+238>: add esp,0x10
0x08048579 <main+241>: sub esp,0xc
0x0804857c <main+244>: push 0x0
0x0804857e <main+246>: call 0x80483c8 <exit>
0x08048583 <main+251>: sub esp,0xc
0x08048586 <main+254>: push 0x8048826
0x0804858b <main+259>: call 0x80483a8 <printf>
0x08048590 <main+264>: add esp,0x10
0x08048593 <main+267>: sub esp,0x8
0x08048596 <main+270>: push 0xbba
0x0804859b <main+275>: push 0xbba
0x080485a0 <main+280>: call 0x80483b8 <setreuid>
0x080485a5 <main+285>: add esp,0x10
0x080485a8 <main+288>: sub esp,0xc
0x080485ab <main+291>: lea eax,[ebp-40]
0x080485ae <main+294>: push eax
0x080485af <main+295>: call 0x8048358 <system>
0x080485b4 <main+300>: add esp,0x10
0x080485b7 <main+303>: leave
0x080485b8 <main+304>: ret
0x080485b9 <main+305>: nop
0x080485ba <main+306>: nop
0x080485bb <main+307>: nop
End of assembler dump.
아직은 어셈블리어에 익숙치 않아, 호출되는 함수를 기준삼아서 흐름을 따라갔다. 일단 ExecuteMe는 문자열이 4줄 출력되고 그 밑에 쉘이 떠있는 것으로만 생각했는데, printf가 4개가 아닌 5개가 사용된다. 왜 [level2@ftz level2$ ] 저게 쉘이 아니라 그저 출력된 문자열일 수도 있다는 것을 생각을 못했을까... main의 시작점을 기준으로 5번째에 있는 printf가 [level2@ftz level2$ ]가 맞는지 확인해보았다.
함수의 주소는 0x80483a8이다.
run을 했더니
Couldn't get registers: 명령이 허용되지 않음.
이라는 경고문이 나온다. 그 이후 disas main과 같은 명령어도 안먹힌다. 실행 권한이 분명 있었는데, 왜 gdb에선 실행이 안될까 싶어서 해당 오류에 대해 찾아보았더니 SetUID가 설정된 파일은 gdb를 이용해 디버깅을 할 수가 없다고 한다. 그렇다면 해당 파일을 복사해서 권한을 풀어서 디버깅을 해야하나 했지만, 그럴 필요없이 해당 파일을 복사해서 복사 파일을 gdb하기만 해도 된다고한다.
tmp 디렉토리로 복사 후 ls -al을 이용해서 권한을 확인해보았다.
SetUID가 해제 되었다. 이제 gdb를 이용해 디버깅 할 차례이다.
run 명령어도 제대로 작동한다. 이제 5번째 printf 함수가 [level2@ftz level2]$ 를 출력하는게 맞는지 확인하면 된다. 먼저 5번째 printf가 호출되는 부분에 Breakpoint를 지정하고 그 곳까지 실행하였다.
0x080484f5 <main+109>: add esp,0x10
0x080484f8 <main+112>: sub esp,0xc
0x080484fb <main+115>: push 0x8048782
0x08048500 <main+120>: call 0x80483a8 <printf>
4번째 printf 호출 이후부터 5번째 printf 호출까지의 어셈블리 코드이다. 함수를 호출하기 전에, 함수의 매개변수를 스택에 넣는다고 하였는데, 위의 코드에서 이에 해당하는 기능을 main+115에서 하는 듯 했다. x/s 0x8048782을 통해 해당 값을 문자열로 표현해보았다.
와우! 예상했던 문자열이 출력되었다. 이제 한결 수월해질 것이다.
해당 문자열을 출력한 이후엔 입력을 받는다. 그 후 결과를 반환하는게 프로그램의 구조인데, main+143에서 fgets 함수를 호출하므로 해당 함수에서 문자열을 입력받을 것이다.
그리고 이후에 main+163에서 strstr 함수를 호출한다. strstr은 입력한 문자열 내에 찾고자 하는 문자열이 있는지를 확인하는 함수이다. 만약 이곳에서 my-pass 또는 chmod 문자열을 찾고 있다면, 내가 생각했던 것이 맞겠구나 라고 생각하였다. strstr 함수에 넘겨준 인자를 살펴보았다. 이 때, 내가 입력한 값은 "AAAAAAAA"이다. strstr 함수는 2개의 매개변수를 가지기 때문에 strstr 바로 직전에 push 해준
0x08048525 <main+157>: push 0x804879c
0x0804852a <main+162>: push eax
이 두개의 값을 확인해 보았다.
역시! my-pass 명령어를 막아 놓은게 아닌 입력 값이 my-pass인 경우만 막아놓은 것이다..! chmod도 볼 필요 없이 똑같겠지만, 2번째 호출되는 strstr의 매개변수도 한번 확인해보도록 한다.
0x08048557 <main+207>: push 0x80487e8
0x0804855c <main+212>: push eax
두번째 strstr 함수 호출 바로 전의 실행문이다. 아까는 eax에 입력값이 들어있었기 때문에 0x080487e8의 값을 확인해본다.
역시 생각대로이다.
정리해보자면, 문장을 출력한 후에 fgets 함수로 문자열을 입력받고, 입력한 문자열이 my-pass이거나 chmod일 때 예외처리를 걸어놓고, 그 외의 경우엔 system 함수의 인자로 넘겨준다고 볼 수 있다.
나의 입력은 my-pass가 아닌 ../level1/tmp/test 였기에 문제 없이 실행된 것이고, 이후에 test 프로그램에서 사용되는 system("my-pass")까지는 확인하지 않기 때문에 비밀번호가 출력 된 것이다..!
비밀번호를 얻어내는 과정은 운 좋게 얻어걸린 감이 없잖아 있지만, 되돌아보는 과정에서 깔끔하게 정리가 되어서 다행이다.
해결되었으니 level2로 고고~!
'Pwnable > FTZ' 카테고리의 다른 글
[ProjectH4C] [Write-up] 해커스쿨 FTZ Level 4 (0) | 2020.08.03 |
---|---|
[ProjectH4C] [Write-up] 해커스쿨 FTZ Level 3 (0) | 2020.08.01 |
[ProjectH4C] [Write-up] 해커스쿨 FTZ Level 2 (0) | 2020.07.31 |
[ProjectH4C] [Write-up] 해커스쿨 FTZ Training 6~10 (0) | 2020.07.30 |
[ProjectH4C] [Write-up] 해커스쿨 FTZ Training 0~5 (0) | 2020.07.30 |