본문 바로가기
잡동사니

시스템 해킹 기초

by L3m0n S0ju 2021. 12. 15.

 

 

Set-UID Privileged Programs

 

일반 사용자가 프로그램을 실행할 때 루트의 권한으로 실행하게 해주는 권한

ex) 일반 사용자가 패스워드를 바꿀 때 root권한이 필요하므로 실행할 때 root 권한으로 실행할 수 있도록 set-uid 설정

 

 

 

 

Set-UID를 사용하는 이유

 

권한을 다이렉트하게 주는 것보다 안전하고 편리함

 

 

 

 

Invoking Program 문제점

 

위 그림과 같이 system 함수로 command를 입력받을 경우 사용자는 aa; /bin/sh와 같이 세미콜론을 이용해 명령어를 이어 붙여 set-uid로 설정된 경우 root 권한의 쉘을 얻을 수 있다.

 

 

 

 

대응방안

 

우분투 16.04 버전 이후로는 /bin/sh가 set-uid가 설정된 프로그램에서 실행되면 권한을 drop한 후 실행되므로 공격이 불가능하다.

 

 

 

 

 

 

execve 함수를 사용하면 데이터와 인자를 분리해서 입력받으므로 세미콜론을 통해 데이터를 이어 붙이면 데이터로 인식되므로 공격이 불가능하다.

 

 

 


 

 

 

 

Capability Leaking

 

위 코드에서 문제점은 fd를 열고 닫지 않았다.

 

 

 

 

 

/etc/zzz에 대해 쓰기 권한이 없지만 cap_leak 프로그램을 실행하면 fd가 닫히지 않았기 때문에 권한이 없지만 cap_leak에 쓰기를 할 수 있다.

 

 

 

 

 


 

 

Dynamic Linker 공격 

 

Dynamic Linking은 프로그램을 컴파일 할 때 모든 코드를 바이너리 형태로 만들기는 용량이 많이 사용되므로 reference하는 함수들은 dynamic linking으로 프로그램을 실행할 때 가져오는 방식이다. 위와 같이 ldd 명령어로 libc.so.6 파일의 위치를 확인할 수 있다.

 

 

 

공격 방법

 

라이브러리 함수들의 위치를 알 수 있는 환경변수 LD_PRELOAD나 LD_LIBRARY_PATH를 수정하면 내가 만든 함수를 우선 실행하도록 설정할 수 있으므로 원하는 코드를 실행할 수 있다.

 

 

 

 

 

 

위 그림과 같이 원래는 sleep(1) 함수를 실행하면 1초 뒤에 프로그램이 종료되지만 공격자가 제작한 printf("I am not sleeping"); 이라는 코드가 실행되도록 라이브러리를 만든다음 LD_PRELOAD에 추가하면 제작한 sleep 함수가 우선 적용되므로 원하는 코드를 실행할 수 있다.

 

 

 

 

 

 

반면 위 그림과 ㄱㅌ이 setuid가 설정되어 euid와 ruid가 다른 경우 LD_PRELOAD와 LD_LIBRARY_PATH 환경변수는 무시하므로 공격이 불가능하다.

 

 

 

 

 


External Program 공격

 

위와 같이 cal 명령어를 실행하는 프로그램이 있을 때 똑같은 이름의 cal이라는 프로그램을 만들어 대신 실행하도록 하겠다.

 

 

 

 

 

그림과 같이 환경변수에 현재 경로를 PATH의 가장 좌측에 삽입하면 가장 우선순위가 높은 경로가 . 경로가 된다. 따라서 system 함수로 cal을 실행하면 .에 cal 프로그램이 있는지 확인하고 있으면 실행하므로 쉘을 획득할 수 있다.

 

 

 

 

 

 


 

Buffer Overflow 공격

그림과 같이 스택은 높은 주소에서 아래 방향으로 쌓이며 전역변수는 Data segment, 초기화되지 않은 전역변수는 BSS 영역에 저장된다.

 

 

 

 

 

 

main 함수에서 foo()를 실행하고 foo() 함수에서 bar() 함수를 실행하면 위와 같은 형태가 된다. 따라서 버퍼오버플로우는 예를 들어 bar() 함수가 끝나고 ret에 있는 return 주소를 통해 foo() 함수로 돌아가는데 ret 위치에 다른 주소값을 주입하여 다른 동작을 유도하는 공격 기법이다.

 

 

 

 

 

 

 

만약 foo() 함수가 위와 같다고 가정하면 str이 100보다 크기가 큰 경우 버퍼오버플로우가 일어난다. strcpy는 크기를 검사하지 않으므로 버퍼오버플로우에 취약한 함수이다. 따라서 버퍼를 뛰어넘어 return address 부분에 우리가 원하는 주소를 주입할 수 있다.

 

 

 

 

버퍼를 넘어서 return 주소를 오염시키고 쉘코드가 있는 곳의 주소를 입력하면 쉘을 획득할 수 있다. NOP는 아무것도 하지 않고 다음으로 넘어가므로 실수로 쉘코드의 주소가 아닌 NOP의 주소로 이동한다고 하더라도 NOP Sled를 타고 쭉쭉 넘어가서 쉘코드에 도착한 후 쉘코드가 실행된다. 여기서 주의할 점은 new return address에 0 값의 바이트가 들어있으면 문자열의 마지막으로 인식하여 strcpy 함수가 종료되므로 주의하자.

 

 

 

요즘 리눅스는 기본적으로 컴파일할때 default로 aslr, nx, canary 보호기법이 적용되므로 버퍼오버플로우 공격을 연습하기 위해서 컴파일할때 스택에서 코드실행을 방지기법을 해제하는 -z execstack 옵션과 카나리를 해제하는 -fno-stack-protector 옵션을 통해 보호기법을 해제해야 한다. aslr은 커널 터미널에서 sudo sysctl -w kernel.randomize_va_space=0 명령어를 통해 해제할 수 있다. 각 보호기법들의 개념은 아래와 같다.

 

 

 

 

ASLR: 스택, 힙, 라이브러리, 등의 주소를 랜덤한 영역에 배치하여, 공격에 필요한 Target address 를 예측하기 어렵게 만든다. 즉, 프로그램이 실행 될 때 마다 각 주소들이 변경된다. 따라서 버퍼 오버플로우를 일으키기 위해서 ret의 주소 값을 한번에 예측해야 한다. 프로그램을 다시 시작하면 주소가 랜덤 값으로 바뀌기 때문에 공격 난이도가 한 단계 올라간다.

 

NX(Non-executable stack): NX특성으로 지정된 모든 메모리 구역은 데이터 저장을 위해서만 사용 되며, 프로세스 명령어가 그 곳에 상주하지 않음으로써 실행되지 않도록 만들어 준다. 따라서 noexecstack 옵션을 설정하면 쉘코드를 스택에서 실행할 수 없고 다른 영역에서 실행해야하므로 공격 난이도가 한 단계 올라간다.

 

StackGuard는 프로그램 중간에 Canary를 삽입하여 버퍼 오버플로가 발생하면 Canary 값이 손상 됬는지 확인하여 오버플로에 대한 경고가 출력되고, 손상된 데이터를 무효화 처리하는 기능이 있 다. 따라서 NOP Sled와 같은 기법을 사용할 때 ret 주변에 Canary를 건드리면 프로그램이 더 이 상 진행되지 않으므로 공격 난이도가 한 단계 올라간다.

댓글