SuNiNaTaS

System Level2(Prob_24)




시스템 파트 2번째 문제입니다.

첫 번째 문제와는 달리 아무런 힌트도 주어지지 않은 상태에서 다운로드만 가능합니다.

다운로드명은 suninatas로, rwxrwxrwx권한이 주어져있지만 실행이 되지 않는 바이너리 형태였습니다.



혹시나 하는 마음에 .zip으로 확장자를 바꾼 후 압축 해제를 했더니 아니나 다를까, *.jar, *.apk를 압축해제 했을 때의 파일 구조를 보여주었습니다.

classes.dex 파일을 dex2jar 툴로 jar파일로 바꿔주도록 합시다.



classes-dex2jar.jar 파일을 jd-gui 툴을 이용하여 열어보았습니다.

해당 jar 파일 안에는 suninatas.class 파일이 존재했는데, 확인 해 보니 localObject1와 localObject2, paramAnonymousView 값에 각각 id, pw, key값을 받아내는 부분이 있었습니다. 그리고 정해진 key값이 들어오면 ~/chk_key.asp로 request요청을 날려주는데, id와 pw, key값을 모두 날려주는 것을 확인 할 수 있었습니다. 빨간 밑줄의 뒷 부분이 key값을 GET 형식으로 보내주는 부분입니다.



해당 url을 그대로 suninatas 웹페이지의 id, pw 그리고 class파일의 key값을 넣어 요청을 때렸더니 Wrong!Wrong!이라는 경고 메시지를 띄워줄 뿐 Auth Key를 뱉어주진 않았습니다.



그래서 직접 request를 때리지 않고 apk파일인만큼 직접 안드로이드에서 돌려보자는 생각을 해 보았습니다.



이클립스의 AVD 툴을 이용해 안드로이드 예뮬 환경 구축 후 직접 돌려보았습니다.



request 요청을 직접 때렸을 때와 달리 Auth Key값을 뱉어내는 것을 확인 할 수 있었습니다.

이번 문제는 가상 안드로이드 환경을 구축 할 수 있는지 없는지의 여부를 확인하는 문제였던 것 같습니다.

'Wargame > SuNiNaTaS' 카테고리의 다른 글

SuNiNaTaS System Level1(Prob_16)  (0) 2016.05.10
SuNiNaTaS Web Level10(Prob_23)  (0) 2016.05.10
SuNiNaTaS Web Level9(Prob_22)  (0) 2016.05.09
SuNiNaTaS Web Level8  (0) 2016.05.09
SuNiNaTaS Web Level7  (0) 2016.05.09

SuNiNaTaS

System Level1(Prob_16)




써니나타스 시스템 카테고리 첫 번째 문제입니다.

SuNiNaTaS.com에 속해있는 멤버의 패스워드를 알아내는 것이 우리의 목표입니다.

Download 버튼을 누르면 packet_dump.pcap 파일이 담겨있는 압축 파일이 받아집니다. 이 파일을 분석하여 패스워드를 알아내도록 하죠.



필터링은 http.request.full_uri contains "suninatas" 로 걸고 suninatas.com에 해당하는 패킷을 살펴보았습니다.

당연히 id/pw를 보내는 요청이기에 POST 요청만 확인하였으며, 맨 아래의 POST 패킷을 제외한 나머지 패킷에는 로그아웃을 한 흔적이 없었습니다.

따라서 맨 아래에 나와있는 id/pw가 실제로 로그인 되는 계정으로 추측 할 수 있습니다.

여기서 구해낸 ultrashark/=SharkPass01 로 suninatas 메인 페이지에서 로그인을 하면 아래와 같이 Key 값을 얻어낼 수 있습니다.



'Wargame > SuNiNaTaS' 카테고리의 다른 글

SuNiNaTaS System Level2(Prob_24)  (0) 2016.05.10
SuNiNaTaS Web Level10(Prob_23)  (0) 2016.05.10
SuNiNaTaS Web Level9(Prob_22)  (0) 2016.05.09
SuNiNaTaS Web Level8  (0) 2016.05.09
SuNiNaTaS Web Level7  (0) 2016.05.09
OverTheWire_Wargame Leviathan

[ level3 -> level4 ]


Leviathan3로 접속을 하면 홈 디렉토리에 level3 바이너리가 우릴 기다리고 있습니다. 해당 바이너리는 leviathan4 권한으로 setuid bit가 걸려있기 때문에 이번 레벨은 level3 바이너리로 풀어내면 될 것 같습니다. 한 번 실행 시켜 보도록 하죠.



바이너리를 실행시키면 패스워드를 요구하는데, 패스워드가 틀렸을 때 오류 메시지를 내뱉고 프로그램을 즉시 종료시킵니다.

긴 문자열을 보내도 segmentation fault같은 메시지를 안 내뱉는 것을 보니 BOF 유형은 아닌 것 같아 보입니다. gdb로 분석해보도록 합시다.



main 함수를 disassemble 한 상태입니다. 중간에 보면 printf로 0x804878f에 들어 있는 정보를 출력합니다. 확인 해 보니 해당 문자열은 "Enter the password> " 였습니다. 이 문자열은 우리가 바이너리를 실행시켰을 때 패스워드를 입력받기 직전에 내뱉는 문자열이었습니다. 따라서 그 이후에 진입하년 do_stuff 함수에 패스워드를 입력 및 비교하는 루틴이 들어 있을 것이라고 예상 할 수 있겠습니다.

그렇다면 do_stuff 함수도 분석 해 보도록 합시다.



중간에 보면 strcmp 함수를 호출하는 것을 볼 수 있습니다. 또한, strcmp 이후 if문의 조건을 통해 루틴을 분기시키는 부분이 있네요. strcmp를 통해 패스워드 일치/불일치 여부를 확인 한 후 이 if문에서 프로그램을 종료시킬 지, 다음 루틴으로 넘어갈 지 판별하는 듯 합니다.

또한, 조금 더 아래쪽을 살펴보면 system 함수가 호출되는 것을 확인 할 수 있는데, 위의 mov 명령을 통해 esp에 들어있는 주소의 문자열을 인자로 함수를 실행시킵니다.

strcmp 함수와 system 함수의 인자에는 어떤 값들이 전달되는 지 확인해보도록 합시다.



strcmp 함수의 1, 2번 인자는 우리가 입력한 문자열과 "snlprintf"입니다. 즉, 우리가 입력한 문자열이 snlprintf일 경우 프로그램을 종료시키지 않고 다음 루틴을 실행한다는 뜻이 되겠죠. 그리고 값이 동일할 경우 0x8048760에 들어있는 문자열을 출력 한 후 0x8048774에 들어있는 문자열을 인자로 system 함수를 실행시킵니다. 0x8048760은 "[You've got shell]!", 0x8048774는 "/bin/sh"인 것을 보니, snlprintf를 입력받았을 경우 전자의 문자열을 출력하고 쉘을 띄워주는 프로그램인 것 같습니다. gdb를 종료하고 프로그램을 실행시켜봅시다.



leviathan4로 통하는 패스워드를 깔끔하게 뱉어줍니다.



OverTheWire_Wargame Leviathan

[ level2 -> level3 ]




레벨2에는 printfile이라는 바이너리가 자리 잡고 있습니다. 레벨1과 마찬가지로 setuid bit가 걸려있기 때문에 이 파일을 이용해서 레벨3로의 패스워드를 알아 내야 할 것입니다.

gdb로 한번 까 보도록 하겠습니다.



먼저 main을 disassemble 한 상태입니다.

위쪽은 파라미터 갯수 검증하는 부분이고, 빨간 체크박스부터 살펴보면 access 함수를 호출하는 것을 알 수 있습니다.

access 함수의 원형을 살펴보죠.

int access(const char *pathname, int mode);

2가지 인자를 받는데, 경로를 포인터로 받고 mode 또한 숫자 또는 상수로 받습니다.


R_OK(0x01)

 읽기 권한 여부

 W_OK(0x02)

 쓰기 권한 여부

 X_OK(0x03)

 실행 권한 여부

 F_OK(0x04)

 파일 존재 여부


함수 호출 규약에 따라 먼저 들어간 0x04가 mode, eax가 *pathname이 되겠군요.

스택에서 한번 어떤 값이 들어가는 지 살펴보도록 하겠습니다.



eax에 들어가는 값을 따라가 보니 argv[1]에 우리가 넣어줬던 파일 경로의 값이 들어갑니다.

그렇다면 여기서 호출하는 access 함수의 목적은 파일이 존재하는지 존재하지 않는지의 여부를 파악하고, 만약 존재하지 않을 경우 프로그램을 종료시키는 것이 될 것입니다.

사실 여기서 바로 /etc/leviathan_pass/leviathan3 로 넣어주면 깔끔하지 않느냐?

라는 질문이 있을 수도 있는데, 이유는 모르겠지만 그렇게 넣어주면 파일 미존재로 인해 프로그램이 종료되었습니다. 뭔가 다른 장치를 해 놓은 듯 합니다.

혹시 아시는 분이 계시면 댓글 부탁드립니다.


자, 그럼 이제 access 함수 이후에 어떤 행동을 하는지 더 살펴보도록 하죠.



access 함수 이후에 snprintf 함수를 호출하는데, sprintf와 동일하지만 버퍼의 길이를 제한하여 넣어준다는 차이가 있습니다.

이 함수는 대충 이런 모양이 될 것 같습니다.


snprintf(buffer, 0x1ff, "/bin/cat %s", argv[1]);


우리가 넣어 준 파일 경로 앞에 /bin/cat을 넣어주고 buffer 변수에 넣는거죠.

그리고 마지막엔 system함수의 인자로 buffer을 넣어줘서 결국 "/bin/cat 파일"을 실행하는 것입니다.

자, 여기까지 이해를 했다면 어떤식으로 문제를 풀어야 할 지 감이 오지 않나요?



/tmp/ 경로에 fstr3am_2라는 디렉토리를 만들어주고, 그 곳에 ".bashrc;sh"라는 이름의 파일을 하나 던져줍니다.

세미콜론 앞에 오는 문자열은 어떤 문자가 오던 간에 상관은 없습니다.

이렇게 한 이유는 access 함수를 우회하기 위해 실제로 존재하는 파일을 생성해주는것이고, 파일명을 /bin/cat에 붙여주기 때문에 결과적으로 버퍼에 들어가는 문자열은 아래와 같이 될 것입니다.


"/bin/cat .bashrc;sh"


이 프로그램에서는 실행을 위해 system 함수를 이용하기 때문에 system("/bin/cat .bashrc;sh") 라는 명령어가 완성이 되는군요.

결과적으로 /bin/cat .bashrc, sh 두 가지의 명령어가 따로 실행이 되는것입니다.



깔끔하게 쉘을 떨어뜨렸습니다. 권한이 leviathan3로 정상적으로 상승된 것을 확인하고, leviathan3의 패스워드를 뱉어내도록 합시다.

Lord of the BufferOverflow Fedora Core3


[ Level3_Dark_eyes -> Hell_fire ]


다음은 Level3의 hell_fire.c입니다.



힌트부터 확인 해 보면 "another fake ebp or got overwriting"이라고 되어 있는데, 이전 문제와 같이 beist's execl 기법을 사용하여 풀이 할 수 있을 것 같습니다.

하지만 소스를 확인 해 보니, remote bof이기 때문에 이 기법을 이용한 풀이는 어려워보입니다. 해당 문제는 standalone으로 바이너리 자체에 hell_fire 권한이 있어 setuid bit가 걸려 있는 것이 아니라, xinetd.d 슈퍼데몬을 이용해 hell_fire 권한으로 해당 소스를 실행하는 형태입니다. 따라서 이전 문제와 같이 symbolic link를 통해 스크립트를 실행시키는 형태가 불가능 할 것입니다.

두 번째인 got overwriting의 경우 ppr gadget을 필요로 하는 것으로 알고 있는데, 코드 영역을 뒤져 보니 pop;pop;leave;ret 와 같은 leave 명령어를 포함한 가젯밖에 없어 got overwriting 기법으로 풀이하지도 못했습니다. fake ebp보다 got overwriting이 풀이하는 데 조금 더 용이 할 것 같아, 관련 문서를 찾아보았지만 결국 찾지 못해 이번 문제는 다른 분들의 write up을 참고하여 풀이하였습니다.

풀이 방법을 요약하자면 fedora core3에서 system함수를 이용하는 것인데, system 함수에서는 내부적으로 do_system 함수를 호출하도록 되어 있습니다. 이 do_system 함수를 따라 들어가보면 내부적으로 execve 함수를 호출하는 부분이 있습니다. 이 때 execve를 실행하는 과정에서 쉘을 띄워주는 부분이 있는데, 이 부분을 이용한 풀이법입니다.

이제, 설명했던 system 함수를 따라 들어가보겠습니다.



system 함수를 disassemble 한 상태입니다. 코드의 끝 부분을 보면 내부적으로 do_system 함수를 call하는 곳이 보입니다. do_system 함수의 주소를 따라 들어가보면 아래와 같은 코드가 있습니다.



위에서 설명했던 execve 함수를 call 하고 있습니다. 그렇다면 여기서 execve를 호출 할 때 어떤 인자들을 받아 실행하기에 쉘을 띄워준다는 것일까요?

이 과정은 http://sanguine.leaveret.kr/7 를 참고하였습니다.

결론부터 말하자면 do_system+1124 부분으로 ret address를 바꿔주면 쉘을 획득 할 수 있습니다. 이 부분은 sigprocmask 함수의 다음 부분인데, sigprocmask 함수를 이용하여 시그널 마스크를 블록 한 후에 내부적으로 쉘을 실행시키는 것 같습니다.

execve 직전에 받는 인자들을 살펴보면 다음과 같습니다.


execve("/bin/sh",{"/home/dark_eyes/hell_fire",NULL},envp)


hell_fire라는 이름으로 /bin/sh를 실행시키는 과정이기 때문에 do_system+1124 부분으로 루틴을 이동시키면 쉘이 띄워지는 원리인 듯 합니다. 정확한 인자값과 구성에 대해서는 후에 공부를 더 해서 연구 해 봐야 할 것 같습니다.


자, 이제 답은 나왔습니다. 이제 payload만 작성하면 끝날 것 같습니다. 하지만 마지막 관문이 남아있습니다. 바로 fgets 함수에 대한 것인데, 이 함수는 인자를 받아들일 때 마지막에 0x0a를 자동으로 받아들입니다. 0x0a는 개행의 아스키코드인데, 이로 인해 0x750784를 payload로 넘겨 줄 경우 0x0a750784로 넘어가게 됩니다. 이에 대해 고민하고 있었는데, 선임 중 한명이 remote 환경에서 파라미터를 전송 할 경우 null byte를 입력 할 수 있다고 하여 그냥 0x00750784로 넘겨 주니 쉘을 획득 할 수 있었습니다.

위 과정을 core dump 하여 확인 해 보도록 하겠습니다.



payload로는 0x713f40(write 함수의 주소)를 사용하였습니다.



위에서 언급 한 대로, 0x0a가 마지막에 추가 된 것을 확인 할 수 있습니다.

이제 마지막에 0x00을 추가 한 buffer의 상태를 보겠습니다.



정상적으로 0x00713f62가 overwrite 된 것을 확인 할 수 있습니다.

그럼 이제 remote 환경에서 do_system+1124 부분을 return address로 하여 exploit 하면 쉘을 획득 할 수 있습니다.



+ Recent posts