Software Security1
Bug vs Vulnerability
Bug: 소프트웨어에서의 모든 결점을 통칭
Vulnerability: 공격자에게 사용될 수 있는 bug
웹 취약점 종류: CSS, CSRF
CSRF(사이트 간 요청 위조, Cross Site Request Forgery):
불특정 다수를 대상으로 로그인 된 사용자가 공격자가 의도한 행위를 하게 된다.
조건 2가지: 위조 요청을 전송하는 서비스에 로그인 된 상태 + 피싱 사이트에 접속
페이스북에 로그인 돼있고(항시 로그인 상태라면) 피싱 사이트의 어떤 버튼이 하는 행위가 페이스북에서 특정한 글을 하는 행위로 연결돼있다면 그 버튼을 누르는 순간 그 계정으로 그 행위가 이루어진다.
방어 방법: Referrer 검증 Security Token 사용(CSRF Token)
Referrer 검증: Back-end에서 request의 referrer을 확인하여 domain과 일치하는 지 확인한다.
Security Token: 사용자의 세션에 난수 값을 저장하고 back-end에서 요청을 받을 때 마다 세션 토큰 값과 요청의 토큰 값이 같은 지 검증한다.
---------------------------------------------------------------------------
Shellcode: 기계어 명령들. 원하는 동작을 하도록 한다. Platform dependent하다.
Buffer overflow:
데이터를 검사하는 과정에서 그 데이터를 저장할 메모리 위치(크기)가 유효한지를 검사하지 않아 발생한다.
데이터가 담긴 위치 근처에 있는 값이 손상되고 프로그램에 영향을 미칠 수 있다.
스택 영역의 BOF로는 RET값을 변조하는 공격이 있다.
BOF가 일어나는 배열에서 RET값이 저장되는 공간까지의 거리를 구해서 RET값을 변조한다.
Use After Free
힙은 런타임 시 할당되는 영역인데 바이너리가 실행되고 죽기 전까지 힘에 데이터가 남아있게 된다.
동적할당 된 heap을 free하고 다시 재사용 할 때 취약점이 발생할 수 있다.
Deferred Coalescing(병합지연): free된 힙이 다음에 realloc될 때 같은 사이즈로 요청받을 수 있다. 이 때 힙을 병합하거나 분할하는 시간을 절약하기 위해 free된 힙을 남겨뒀다가 재사용할 때 그대로 쓰게 한다.
Return-to-Libc
RTL은 return address에 libc내의 함수를 덮어씌워 쉘코드 없이 exploit한다.
ROP(Return Oriented Programming)
Gadget을 사용한다. ret로 끝나는 명령 조각들을 이용한다.
Gadget들을 이용해서 하나의 프로그램처럼 동작하게 한다.
Automated ROP: 스택/힙에서 쉘코드를 실행하는 것이 아니라 gadget으로 간 다음 실행한다.
---------------------------------------------------------------------------
메모리 보호 기법
ASLR: Address Space Layout Randomization
메모리상의 공격을 어렵게 하기 위해 libc 및 데이터 등 메모리 주소를 실행할 때 마다 랜덤으로 바뀌게 하는 기법이다.
하지만 코드의 시작점에서만 randomization이 일어나기 때문에 그이후 메모리가 leak되는 경우 해당 libc에서 함수의 offset를 계산해서 공격이 가능하다.(fine-graind ASLR은 이것도 막는 듯)
Base 주소가 매번 달라지는 거지 내부 offset은 libc에 종속적이기 때문에 변하지 않는다.
ASLR해제 명령: echo 0 > /proc/sys/kernel/randomize_va_space
DEP: Data Execution Prevention
데이터 영역에서 코드가 실행되는 것을 막는다.
스택의 BOF로 return address를 스택상의 한 주소로 변경했어도 실행권한이 없기 때문에 예외처리 후 종료된다.
스택 실행 권한 설정: 컴파일 시 -z execstack
ASCII-Armor
공유라이브러리 영역의 상위주소에 oxoo를포함시키는 방법이다.
RTL(return to library)공격에 대응하기 위한 방법이다.
공격자가 라이브러리를 호출하는 BOF 공격을 해도 NULL바이트가 삽입된 주소로는 접근할 수 없다.
Stack Canary
함수 진입 시 스택에 SFP(Saved Frame Pointer)와 return address 정보를 저장할 때 이 정보들이 덮어씌어지는 것을 방지하기 위해 스택상의 변수들의 공간과 SFP 사이에 Canary라는 특정한 값을 추가한다.
Canary를 사용하면 함수 진입 시 Buffer - Canary - SFP - RET의 순서로 스택에 저장돼있고, Canary에는 다른 곳에서 얻어온 값을 저장한다.
return되어 종료될 때 Canary값과 원본과 비교해서 다르면 프로그램을 종료한다.
---------------------------------------------------------------------------
Meltdown
사용자 프로그램이 커널이 사용하는 메모리 내용을 알아낼 수 있다.
Cpu의 예측 실행 기능(Speculative Execution)과 비순차적 실행(out-of-order execution)이 동작하면서 메모리 참조에 대한 권한 체크가 제대로 이루어지지 않는다.
비순차적 실행: 한 명령이 실행되고 순차적으로 다음 명령을 실행하는 것 보다 다음 명령이 현재 명령에 의존적이라면 기다리는 시간이 필요하므로 비의존적인 명령을 먼저 실행해서 총 사이클 수를 줄이는 것이다.
Speculative execution: 커널 메모리 주소를 직접 요청하는 코드가 두 줄 있을 때 첫번째 줄에서 인터럽트가 발생해서 폐기가 되므로 두번째 명령도 commit하지 않는다. 하지만 예측 실행으로 두번째 명령이 실행은 된다(실행은 되지만 사용자에게 보여주지는 않는다.)
그래도 명령은 실행되기 때문에 두번째 명령의 실행결과는 캐시에 로드가 된다.
따라서 이후 메모리 주소들을 쫙 읽어보면 빠르게 접근되는 값이 있는데 그 값이 두번째 명령에서 load한 값이다.
두번째 명령이 커널 메모리 rax나 이런 거에 접근하는 명령이었으면 그 주소가 캐싱돼있고 나중에 메모리들을 쫙 읽으면서 rax의 주소값을 알아낼 수 있다.
커널에서 사용하는 메모리 영역과 일반 프로그램에서 사용하는 메모리 영역을 운영체제 차원에서 분리를 하면 해결이 가능하지만 캐시 효율이 저하되어 성능이 떨어진다.
CFI(Control Flow Integrity)
White list approach. White list에 있는 control flow만 runtime에 허락한다.
컴파일 할 때 어떤 jump가 갈 수 있는 모든 곳에 label을 표시하고 동작할 때 label이 있는 지 확인한다.
동적 라이브러리 시에는 어렵다.
DFI(data Flow Integrity)
Legitimate writer가 데이터에 태그를 남긴다.
Reader는 태그가 있는 지 없는 지 확인을 한다.
어떻게 모드 legitimate data flow를 알 지가 관건이다.
---------------------------------------------------------------------------
No false negative: Soundness
No false positive: Completeness
Symbolic Execution: 모든 input을 symbolize한 채로 실행한다.
Concrete Execution: 모든 input을 concrete한 채로 실행한다.
Driller: fuzzing과 symbolic execution을 합친 것이다. Fuzzing하다가 안되는 곳은 symbolic하게 돈다.
Random Fuzzing: input을 bit/byte level로 바꾼다. Strict format/header requirement가 없을 때 유용하지만 strong constraint에 막히기 쉽다(인풋을 항상 체크하는 경우).
Grammar-based Fuzzing: 프로그램에 맞는 input을 만든다. 복잡한 format의 인풋도 만들 수 있지만 프로그램에 대한 지식이 필요하다.
Genetic-based Fuzzing: input의 goodness를 나타내는 함수를 만든다. 랜덤하게 만들어진 input들에서 이 함수의 결과값이 일정 수준 이상이면 corpus(a set of inputs to be mutated)에 추가한다.
Coverage-based Fuzzing: Genetic-based의 특별한 케이스이다. input이 new code block을 실행하는 지 본다. Strong constraint에 막히기 쉽다.
생성기반 vs 변형기반: 변형기반은 더 적합한 입력값을 넣을 수 있지만 샘플이 필요하고 샘플에서 크게 벗어난 인풋은 만들기 어렵다.
Dumb vs Smart: 퍼징 대상의 입력 구조를 아는 지 모르는 지
White vs Gray vs Black: 퍼징 대상 프로그램의 구조를 아는 지
AFL: Coverage-based, user-level fuzzer.
Syzkaller: Coverage-based, kernel fuzzer.
Bug: 소프트웨어에서의 모든 결점을 통칭
Vulnerability: 공격자에게 사용될 수 있는 bug
웹 취약점 종류: CSS, CSRF
CSRF(사이트 간 요청 위조, Cross Site Request Forgery):
불특정 다수를 대상으로 로그인 된 사용자가 공격자가 의도한 행위를 하게 된다.
조건 2가지: 위조 요청을 전송하는 서비스에 로그인 된 상태 + 피싱 사이트에 접속
페이스북에 로그인 돼있고(항시 로그인 상태라면) 피싱 사이트의 어떤 버튼이 하는 행위가 페이스북에서 특정한 글을 하는 행위로 연결돼있다면 그 버튼을 누르는 순간 그 계정으로 그 행위가 이루어진다.
방어 방법: Referrer 검증 Security Token 사용(CSRF Token)
Referrer 검증: Back-end에서 request의 referrer을 확인하여 domain과 일치하는 지 확인한다.
Security Token: 사용자의 세션에 난수 값을 저장하고 back-end에서 요청을 받을 때 마다 세션 토큰 값과 요청의 토큰 값이 같은 지 검증한다.
---------------------------------------------------------------------------
Shellcode: 기계어 명령들. 원하는 동작을 하도록 한다. Platform dependent하다.
Buffer overflow:
데이터를 검사하는 과정에서 그 데이터를 저장할 메모리 위치(크기)가 유효한지를 검사하지 않아 발생한다.
데이터가 담긴 위치 근처에 있는 값이 손상되고 프로그램에 영향을 미칠 수 있다.
스택 영역의 BOF로는 RET값을 변조하는 공격이 있다.
BOF가 일어나는 배열에서 RET값이 저장되는 공간까지의 거리를 구해서 RET값을 변조한다.
Use After Free
힙은 런타임 시 할당되는 영역인데 바이너리가 실행되고 죽기 전까지 힘에 데이터가 남아있게 된다.
동적할당 된 heap을 free하고 다시 재사용 할 때 취약점이 발생할 수 있다.
Deferred Coalescing(병합지연): free된 힙이 다음에 realloc될 때 같은 사이즈로 요청받을 수 있다. 이 때 힙을 병합하거나 분할하는 시간을 절약하기 위해 free된 힙을 남겨뒀다가 재사용할 때 그대로 쓰게 한다.
Return-to-Libc
RTL은 return address에 libc내의 함수를 덮어씌워 쉘코드 없이 exploit한다.
ROP(Return Oriented Programming)
Gadget을 사용한다. ret로 끝나는 명령 조각들을 이용한다.
Gadget들을 이용해서 하나의 프로그램처럼 동작하게 한다.
Automated ROP: 스택/힙에서 쉘코드를 실행하는 것이 아니라 gadget으로 간 다음 실행한다.
---------------------------------------------------------------------------
메모리 보호 기법
ASLR: Address Space Layout Randomization
메모리상의 공격을 어렵게 하기 위해 libc 및 데이터 등 메모리 주소를 실행할 때 마다 랜덤으로 바뀌게 하는 기법이다.
하지만 코드의 시작점에서만 randomization이 일어나기 때문에 그이후 메모리가 leak되는 경우 해당 libc에서 함수의 offset를 계산해서 공격이 가능하다.(fine-graind ASLR은 이것도 막는 듯)
Base 주소가 매번 달라지는 거지 내부 offset은 libc에 종속적이기 때문에 변하지 않는다.
ASLR해제 명령: echo 0 > /proc/sys/kernel/randomize_va_space
DEP: Data Execution Prevention
데이터 영역에서 코드가 실행되는 것을 막는다.
스택의 BOF로 return address를 스택상의 한 주소로 변경했어도 실행권한이 없기 때문에 예외처리 후 종료된다.
스택 실행 권한 설정: 컴파일 시 -z execstack
ASCII-Armor
공유라이브러리 영역의 상위주소에 oxoo를포함시키는 방법이다.
RTL(return to library)공격에 대응하기 위한 방법이다.
공격자가 라이브러리를 호출하는 BOF 공격을 해도 NULL바이트가 삽입된 주소로는 접근할 수 없다.
Stack Canary
함수 진입 시 스택에 SFP(Saved Frame Pointer)와 return address 정보를 저장할 때 이 정보들이 덮어씌어지는 것을 방지하기 위해 스택상의 변수들의 공간과 SFP 사이에 Canary라는 특정한 값을 추가한다.
Canary를 사용하면 함수 진입 시 Buffer - Canary - SFP - RET의 순서로 스택에 저장돼있고, Canary에는 다른 곳에서 얻어온 값을 저장한다.
return되어 종료될 때 Canary값과 원본과 비교해서 다르면 프로그램을 종료한다.
---------------------------------------------------------------------------
Meltdown
사용자 프로그램이 커널이 사용하는 메모리 내용을 알아낼 수 있다.
Cpu의 예측 실행 기능(Speculative Execution)과 비순차적 실행(out-of-order execution)이 동작하면서 메모리 참조에 대한 권한 체크가 제대로 이루어지지 않는다.
비순차적 실행: 한 명령이 실행되고 순차적으로 다음 명령을 실행하는 것 보다 다음 명령이 현재 명령에 의존적이라면 기다리는 시간이 필요하므로 비의존적인 명령을 먼저 실행해서 총 사이클 수를 줄이는 것이다.
Speculative execution: 커널 메모리 주소를 직접 요청하는 코드가 두 줄 있을 때 첫번째 줄에서 인터럽트가 발생해서 폐기가 되므로 두번째 명령도 commit하지 않는다. 하지만 예측 실행으로 두번째 명령이 실행은 된다(실행은 되지만 사용자에게 보여주지는 않는다.)
그래도 명령은 실행되기 때문에 두번째 명령의 실행결과는 캐시에 로드가 된다.
따라서 이후 메모리 주소들을 쫙 읽어보면 빠르게 접근되는 값이 있는데 그 값이 두번째 명령에서 load한 값이다.
두번째 명령이 커널 메모리 rax나 이런 거에 접근하는 명령이었으면 그 주소가 캐싱돼있고 나중에 메모리들을 쫙 읽으면서 rax의 주소값을 알아낼 수 있다.
커널에서 사용하는 메모리 영역과 일반 프로그램에서 사용하는 메모리 영역을 운영체제 차원에서 분리를 하면 해결이 가능하지만 캐시 효율이 저하되어 성능이 떨어진다.
CFI(Control Flow Integrity)
White list approach. White list에 있는 control flow만 runtime에 허락한다.
컴파일 할 때 어떤 jump가 갈 수 있는 모든 곳에 label을 표시하고 동작할 때 label이 있는 지 확인한다.
동적 라이브러리 시에는 어렵다.
DFI(data Flow Integrity)
Legitimate writer가 데이터에 태그를 남긴다.
Reader는 태그가 있는 지 없는 지 확인을 한다.
어떻게 모드 legitimate data flow를 알 지가 관건이다.
---------------------------------------------------------------------------
No false negative: Soundness
No false positive: Completeness
Symbolic Execution: 모든 input을 symbolize한 채로 실행한다.
Concrete Execution: 모든 input을 concrete한 채로 실행한다.
Driller: fuzzing과 symbolic execution을 합친 것이다. Fuzzing하다가 안되는 곳은 symbolic하게 돈다.
Random Fuzzing: input을 bit/byte level로 바꾼다. Strict format/header requirement가 없을 때 유용하지만 strong constraint에 막히기 쉽다(인풋을 항상 체크하는 경우).
Grammar-based Fuzzing: 프로그램에 맞는 input을 만든다. 복잡한 format의 인풋도 만들 수 있지만 프로그램에 대한 지식이 필요하다.
Genetic-based Fuzzing: input의 goodness를 나타내는 함수를 만든다. 랜덤하게 만들어진 input들에서 이 함수의 결과값이 일정 수준 이상이면 corpus(a set of inputs to be mutated)에 추가한다.
Coverage-based Fuzzing: Genetic-based의 특별한 케이스이다. input이 new code block을 실행하는 지 본다. Strong constraint에 막히기 쉽다.
생성기반 vs 변형기반: 변형기반은 더 적합한 입력값을 넣을 수 있지만 샘플이 필요하고 샘플에서 크게 벗어난 인풋은 만들기 어렵다.
Dumb vs Smart: 퍼징 대상의 입력 구조를 아는 지 모르는 지
White vs Gray vs Black: 퍼징 대상 프로그램의 구조를 아는 지
AFL: Coverage-based, user-level fuzzer.
Syzkaller: Coverage-based, kernel fuzzer.
댓글
댓글 쓰기