엄청나게 삽질을 한 문제다.
일단 소스를 보면 전과 다르게 엄청나게 길어진 것을 볼 수 있다.
소스를 잘 보면 결국 execve함수의 주소를 구한뒤 이 값이 ret값이랑 일치해야 기본적으로 익스를 진행할 수 있는 구조다. 그렇다면 execve를 활용해서 쉘을 따야된다는 건데 execve함수를 잘 몰라서 찾아보니 이런 형태의 함수였다.
int execve(const char *path, char *const argv[], char *const envp[]); |
path에 지정한 경로명의 파일을 실행하며 argv, envp를 인자로 전달한다. argv와 envp는 포인터 배열이다. 이 배열의 마지막에는 NULL 문자열을 저장해야 한다. |
요 형식대로라면 첫번째 인자로 /bin/sh 문자열 주소를 넣어주고, 두번째 인자로는 {/bin/sh문자열주소,NULL문자열 주소}, 세번째 인자로는 환경변순데 NULL을 줘도 무관해서 결국 NULL문자열의 주소를 사용하면 될 것 같았다.
요런식으로 간단하게 execve를 이용해서 쉘을 띄우는 소스를짜서 실제로 디버깅을 해봤다.
execve 함수의 인자값을 보면 첫번째 인자인 0x8048450 주소값을 보면 /bin/sh 문자열이 저장되어 있었고 두번째 인자인 eax값의 담긴 주소값을 보면 /bin/sh문자열 이후 NULL값이 담겨있는걸 실제로 확인할 수 있었다. (두번째 포인터형 배열인 argv에서 *argv[0], *argv[1] 영역에 /bin/sh , NULL의 문자열 주소값이 아닌 그냥 값이 담겨있었던 이유는 잘 모르겠음. 전체적인 메모리구조 및 포인터에 대한 지식이 너무 부족함.)
그럼이제 다음과 같이 총3가지 재료들을 모아주면 된다.
execve 주소
/bin/sh 문자열 주소
/bin/sh 문자열 주소 + NULL값이 있는 주소
execve 주소는 기존 코드에 의하면 값을 구하는과정에서 /home/giant/assassin 이라는 파일에 대한 읽기 권한이 필요한데 현재 권한에서는 해당 파일의 읽기권한이 없어 코드를 그대로 실행했을 경우 제대로된 execve값이 세팅이 안되기 때문에 소스에서 /home/giant/assassin 파일을 /home/bugbear/giant 파일로 바꾼후 printf로 execve값을 출력하도록 수정한뒤 컴파일한 파일로 구했다.
execve 주소 = 0x400a9d48
그 다음 /bin/sh 문자열 주소를 구해야되는데 간단하게 환경변수에 올려놓고 주소값 구하는 방식을 사용했다.
마지막으로 가장 큰 문제였던 두번째 인자값을 구해야 했는데 스택 내 맨 마지막에 프로그램명+NULL이 저장되는 것을 활용했다.
위의 두가지 개념을 생각해논 다음 먼저 환경변수에 /bin/sh를 올려놓고 주소값을 구했다. 이 때 실행파일명에 주소값이 영향을 받으므로 최종적으로 익스할때 쓸 실행파일명이 4바이트주소값이므로 임의의 4바이트 주소값으로 실행파일명을 정해논다음 해당파일에서 환경변수값을 구했다.
세그먼트 에러낸 후 코어파일에서 정확한 주소값을 구했다.
/bin/sh 문자열 주소 = 0xbfffff3b
마지막으로 두번째 인자값의 주소는 스택내 맨 마지막 부분의 주소를 위의 코어파일을 통해 구했다.
/bin/sh 문자열 주소 + NULL 주소 값이 있는 주소 = 0xbffffff7
이제 실행파일명을 /bin/sh문자열이 있는 주소로 변경한뒤 구해논 재료들을 가지고 익스플로잇하면 된다.
? 완벽하게 구했다고 생각했는데 execve값이 계속 틀리다고 나왔다. 여기서 엄청난 삽질을 하다 멘탈이 나갔는데 결국 구글링해서 알고보니 execve 주소값에 들어간 \x0a값이 개행문자로 들어가서 그 뒤에 값들이 제대로 인자값으로 안들어간거였다.
인자값을 ""로 묶어주면 %0a가 개행으로 안들어가서 익스할수있었다.
이제 심볼릭 링크걸고 익스해주면 된다.