: 시스템 해킹을 다른 말로 포너블(pwnable)이라고 한다.
시스템 해킹: 운영체제나 소프트웨어, 하드웨어에 내재된 보안 취약점을 해킹하는 것
시스템 해킹(포너블)의 목표 : 시스템의 일반 유저 자격으로, 여러 가지 공격 기법을 활용해 관리자 권한 탈취
관리자 권한을 얻기 위해 사용하는 함수: system("/bin/sh"), execve("/bin/sh”)
➔ 이것들을 바이트코드로 옮긴 것이 쉘코드
➔ 즉 관리자 권한을 얻기 위해 쉘을 열어야 하고, 쉘을 여는 함수들이 필요하다.
포너블의 과정
1. 프로그램 취약점 발견
2. 취약점 기반 공격 시나리오 작성
3. 공격 시나리오를 기반으로 하여 필요한 정보 수집 및 공격
(+ 메모리 구조 분석이 필수적)
4. 공격 성공 후 권한을 탈취하거나 원하는 정보 얻음
포너블을 위한 선수 지식
포너블을 하기 위해서는 시스템에 돌아가는 프로그램을 분석하고, 취약점을 찾아서 공격해야 하므로 C언어 해석능력, 프로그램 메모리 구조, 리눅스 지식, 리버싱 능력, 쉘코드 작성 등등 여러 지식 요구
: 프로그램에 정의된 특정 영역으로, 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것.
운영체제가 하나의 프로세스를 실행시키면,
➔ 프로세스를 segment라는 단위로 묶어 가용 메모리 영역(= 유저 영역)에 저장
➔ 프로세스 실행 시점에 실제 메모리의 어느 위치에 저장될 지가 결정
+)유저 영역에는 여러 segment가 들어갈 수 있고 병렬적으로 작업 수행(멀티태스킹 가능) / 디버깅 할 때 위치 결정 X, 실행 할 때 위치가 결정 O
리눅스에서는 프로세스의 메모리를 크게 5가지의 세그먼트(Segment)로 구분.
코드 세그먼트(Code Segment)/텍스트 세그먼트(Text Segment)
:실행 가능한 기계 코드가 위치하는 영역으로 읽기와 실행 권한이 있다.
쓰기 권한이 있으면 공격자가 악의적인 코드를 삽입하기가 쉬워지므로, 대부분의 현대 운영체제는 이 세그먼트에 쓰기 권한을 제거한다.
ex) main() 등의 함수 코드
데이터 세그먼트(Data Segment)
: 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치하며, 읽기 권한이 있다.
: 데이터 세그먼트는 쓰기가 가능한 세그먼트(data 세그먼트)와 쓰기가 불가능한 세그먼트(rodata 세그먼트)로 다시 분류된다.
data 세그먼트: 쓰기가 가능한 세그먼트 (읽기 + 쓰기)
ex) 초기화 된 전역변수
rodata(read-only data) 세그먼트: 쓰기가 불가능한 세그먼트
프로그램이 실행되면서 값이 변하면 안되는 데이터들이 위치.
ex) 전역으로 선언된 상수 등
BSS 세그먼트(BSS Segment, Block Started by Symbol Segment)
: 컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 메모리 영역으로, 읽기와 쓰기 권한이 있다.
ex) 개발자가 선언만 하고 초기화하지 않은 전역변수 등
: BSS 세그먼트의 메모리 영역은 프로그램이 시작될 때, 모두 0으로 값이 초기화 된다.
ex) C 코드를 작성할 때, 초기화되지 않은 전역 변수의 값은 0이 된다.
스택 세그먼트(Stack Segment)
: 프로세스의 스택이 위치하는 영역으로, 함수의 인자나 지역 변수와 같은 임시 변수들이 실행 중 여기에 저장된다. 읽기, 쓰기 권한이 있다.
: 스택 프레임(Stack Frame)이라는 단위로 사용된다.
➔ 함수가 호출될 때 생성, 반환될 때 해제.
: 어떤 프로세스가 실행될 때, 이 프로세스가 얼마 만큼의 스택 프레임을 사용하게 될 지를 미리 계산하는 것은 일반적으로 불가능하다.
➔ 운영체제는 프로세스를 시작할 때 작은 크기의 스택 세그먼트를 먼저 할당해주고, 부족해 질 때마다 이를 확장.
힙 세그먼트(Heap Segment)
: 데이터가 위치하는 세그먼트로 읽기와 쓰기 권한이 부여된다.
실행 중 동적으로 할당될 수 있으며, 리눅스에서는 스택 세그먼트와 반대 방향으로 자란다.
ex) malloc(), calloc() 등으로 할당 받은 메모리
연습)
a가 위치하는 세그먼트 : a는 초기화된 전역변수 이므로 데이터에 위치
b가 위치하는 세그먼트 : b는 const 값으로 선언된 전역변수이므로 rodata에 위치
c가 위치하는 세그먼트 : c는 값이 정해지지 않은 전역변수이므로 BSS에 위치
d가 위치하는 세그먼트 : d는 foo함수의 값이 정해진 지역변수이므로 스택에 위치
e가 위치하는 세그먼트 : e는 main함수의 동적할당된 메모리이므로 힙에 위치
foo가 위치하는 세그먼트 : foo는 함수이므로 코드에 위치
"d_str"이 위치하는 세그먼트 : "d_str" 문자열은 상수 문자열로 취급되어 rodata에 위치
: 메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점
: 버퍼를 넘어버렸다
버퍼 오버프로우가 문제인 이유
➔ 스택 메모리에는 내가 정한 버퍼 뿐만 아니라, 다른 변수들도 많이 있기 때문에 다른 변수의 메모리 영역을 침범하여 이 변수들의 값을 해커가 원하는 값으로 변경할 수 있게 되고, 그러면 프로그램의 흐름을 원하는 대로 조작 가능하기 때문에
➔ 스택메모리에는 변수들 뿐만 아니라 함수가 끝난 후 실행할 ’리턴주소’라는 부분이 있어서 main함수를 포함한 함수들은 종료되고 나면 이 리턴 주소에 있는 부분으로 돌아가 다시 실행을 한다. 이 리턴 주소를 버퍼오버플로우로 조작하는 경우 실행되어서는 안되는 함수가 실행되거나, 프로그램에서 사용하지 않은 함수를 사용할 수 있다
버퍼오버플로우 예시
system("/bin/sh")
system 함수: 인자로 받은 문자열을 쉘안에서 실행시킨다.
➔system이라는 함수에 “/bin/sh”라는 인자를 주면서 실행중
쉘 안에서 /bin/sh/라는 명령어 사용 시, 그 쉘을 다시 한 번 얻어서 실행한다
프로그램 내에서 이 코드를 실행하면 프로그램 사용자가 쉘을 탈취 가능
ls -l 명령어로 level1안에 들어 있는 파일 확인.
hint 라는 파일 존재
cat hint 명령어로 hint파일 보기
find 명령어로 level2의 setuid가 걸린 파일 찾기 ➔ /bin/ExecuteMe 존재
find / -user level2 -perm -4000 2>/dev/null
/: 전체 시스템(root directory)에서 파일 검색
-user n: 파일 소유자 명이 n인 파일을 검색
-perm: ~한 권한으로 찾는다
-4000: 최소 4000 permission이 설정되어 있는 파일부터 그 이상으로 찾는다. (4는 setuid 의미)
2>/dev/null: 표준 에러가 발생하면 이를 휴지통으로 보낸다
ExecuteMe가 있는 bin으로 이동 후 실행
/bin/bash 명령어를 통해 쉘을 실행
my-pass를 입력해 level2의 비밀번호 얻기. 비밀번호 : "hacker or cracker"
ls -l, cat hint 명령어로 hint 보기
find / -user level3 -perm -4000 2> /dev/null 명령어로 소유주가 level3인 파일 검색 ➔ /user/bin/editor 존재
해당 파일로 이동 후 실행하면 vi편집기 창이 뜬다.
(vi의 ex모드에서 !를 사용하면 리눅스 명령어가 실행)
:!my-pass를 입력하면 비밀번호 "can you fly?" 얻음.
autodig의 소스를 보면 프로그램을 실행할 때 입력한 값이 cmd 배열에 복사되고 cmd가 system상에서 작동한다는 것을 알 수 있다. => cmd에 내가 원하는 명령어를 넣어 system 상에서 작동하게 만들면 된다.
more hints에서 여러 명령어를 동시에 사용하기 위해서는 세미콜론을 사용하면 되소, 문자열 형태로 명령어를 전달하기 위해서는 따옴표를 사용하면 된다.
autodig 위치를 찾은 후 /bin으로 이동해서 autodig를 실행
autodig에 아무런 인자도 주지않고 실행시키면 사용법을 출력
sh;my-pass를 인자로 전해 ./autodig "sh;my-pass"를 입력하면 비밀번호 출력
비밀번호: "suck my brain"
hint를 통해 /etc/xined.d/에 이동 후 cat backdoor을 통해 백도어 파일 확인. 코드를 보면 finger라는 service를 통해 level5의 권한으로 /home/level4/tmp/backdoor가 실행된다는 것을 알 수 있다.
/home/level4/tmp로 이동한 후 ls -al 명령어를 이용해 디렉토리를 확인. 그러나 backdoor 파일은 존재X. 따라서 vi editor를 이용해 backdoor.c 파일을 만들어 준다.
#include <stdio.h>
int main() {
system("my-pass");
return 0;
}
컴파일 하면 (gcc -o backdoor backdoor.c) 비밀번호 "what is your name?"을 알 수 있다.
hint 파일을 찾아서 보면 이번 level은 /tmp디렉토리에 level5.tmp라는 임시파일을 생성해야 한다는 것을 알 수 있다.
tmp 디렉토리에서 cat > level5.tmp 명령으로 level5.tmp를 덮어쓴 다음 다시 실행하면 비밀번호 "what the hell"을 알 수 있다.