OverTheWire Behemoth 

[ level1 -> level2 ]


behemoth 레벨1입니다. narnia와 달리 소스코드가 주어지지 않아 gdb로만 분석을 해야하는 불편함이 있습니다.

바로 확인 해 보도록 합시다. 



/behemoth 디렉토리로 이동하여 behemoth1 바이너리를 실행시켰을 때의 결과입니다.

패스워드를 요구하며, 패스워드에 부합하지 않을 시 "Authentication failure." 라는 메시지를 내뱉고 프로그램을 종료시키는 듯 합니다.

소스코드가 없으니 gdb를 이용해서 main 부분을 확인 해 보도록 하죠.



main 함수를 보니 인증 부분 자체가 없습니다. 그냥 gets 함수로 값을 입력받고, puts 함수로 오류 메시지를 내뱉고 종료시키네요.

인증이고 뭐고 BOF를 이용해서 쉘을 띄우라는 의도인 것 같습니다.

BOF를 위해 입력받은 값이 어디로 들어가는 지 확인 해 보았습니다.



ebp-0x50 부분을 확인 해 보니, 0xffffd5fe부터 값이 차례로 들어가는 것을 알 수 있었습니다.

이제 main 함수의 return address를 찾아야 하는데, 하나씩 확인 해 보니 0xf7e3da63 부분이 __libc_start_main 함수로 이어지는 것을 알 수 있었습니다.

다음 그림을 보도록 하죠.



__libc_start_main 함수에서 main 함수를 호출 한 이후 모든 루틴을 끝냈을 때 다시 __libc_start_main 함수로 돌아와 exit함수를 호출하며 프로그램을 종료시키는 형태입니다.

이를 return address로 간주하고 풀이하도록 하겠습니다.

이 return address와 buffer 사이의 간격이 79byte이기 때문에 문자 'A'를 총 79번 입력 한 이후 쉘코드가 있는 주소를 적어주면 쉘이 띄워질 것입니다.



gdb상에서 A를 79번 입력하기 위해 임시로 'a'라는 파일을 만들어 주었습니다.



gdb로 실행시켜 해당 return 값의 직전까지 값이 들어가는 것을 확인한 결과 화면입니다.

다음으로, 쉘코드를 담을 공간을 확보하는 것인데, 첫번째 방법으로는 우리가 A로 집어 넣은 버퍼 공간에 쉘코드를 집어 넣는 방법이 있으며, 두번째는 환경변수를 이용하여 쉘코드를 입력 한 후, 그 주소를 ret 값에 덮어 씌우는 방식입니다. 저는 두번째 방법인 환경변수를 이용하는 공격 기법으로 풀이하도록 하겠습니다.



export 명령어를 이용해 EGG라는 이름의 환경변수를 만들어주고, 그 값으로 0x90을 100번 반복하고 그 뒤에 쉘코드를 입력해주었습니다. 0x90을 여러번 입력 해 주는 이유는 0x90이 NOP(아무 행동도 하지 않고 다음에 나오는 명령을 실행)의 의미이기 때문에 주소의 오차 범위를 줄임과 동시에, 스택 상에서 환경변수의 주소를 찾기 용이하게 하기 위해 입력 해 주었습니다.



gdb에서 스택을 확인하였더니 그림과 같이 0x90의 반복되는 부분을 확인 할 수 있었습니다. 이 중 아무 주소나 선택하면 되는데, 저는 0xffffd8b8을 리턴 주소로 선택하여 exploit 하도록 하겠습니다.



위에서 구한 EGG 환경변수의 주소를 ret 값에 덮어 씌워주면 behemoth2 권한의 쉘을 띄울 수 있습니다.





OverTheWire_Wargame Narnia

[ level4 -> level5 ]



이번에는 환경변수를 사용하지 못하도록 NULL Byte를 채워 넣는 Egg hunter가 추가된 BOF 문제입니다.

스택의 주소는 이전과 동일하게 고정적이므로 nop sled와 shellcode를 이용하여 풀이하겠습니다.



먼저 값을 충분히 집어넣어 segmentation fault가 터지는 지점을 찾아냈습니다.

272바이트를 입력했을 경우 segmentation fault가 나타나는데, 이는 SFP가 0x91919191로 덮어져 잘못된 주소로 리턴했기 때문에 나타나는 증상이죠.

그러므로 buffer+272의 위치가 return address입니다.



shellcode는 24byte짜리 shellcode를 사용 할 것이며, 272 - 24 = 248byte 만큼을 0x90으로 채워주고 리턴 할 위치를 찾아보도록 하겠습니다.



ebp+0x0c에서 +4만큼의 위치에 있는 argv[1]의 값을 찾아냈습니다.

그 중 0xffffd7df를 return address로 잡도록 하겠습니다.


return address : 0xffffd7df



payload :

./narnia4 0x90*248 + shellcode(24byte) + 0xffffd7df



짜잔!

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