자격/SW보안약점진단원
구현단계 보안약점 제거 기준-경로 조작 및 자원 삽입
Ignatius Heo
2023. 7. 3. 04:52
작성일: 230703
※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.
정리 내용: 소프트웨어 보안약점 진단 가이드(201~210p)
구분 | - 입력데이터 검증 및 표현 |
설계단계 | - 시스템 자원 접근 및 명령어 수행 입력값 검증 https://cryptocurrencyclub.tistory.com/92 |
개요 | 검증되지 않은 외부입력값으로 파일 및 서버 등 시스템 자원에 대한 접근 혹은 식별을 허용할 경우, 입력값 조작으로 시스템이 보호하는 자원에 임의로 접근할 수 있는 보안약점이다. 경로조작 및 자원삽입 약점을 이용하여 공격자는 자원의 수정, 삭제, 시스템 정보누출, 시스템 자원 간 충돌로 인한 서비스 장애 등을 유발시킬 수 있다. 즉, 경로 조작 및 자원 삽입으로 공격자가 허용되지 않은 권한을 획득하여, 설정에 관계된 파일 을 변경 하거나 실행시킬 수 있다. |
진단 세부사항 (설계단계) |
① 외부 입력값을 이용하여 시스템자원을 식별하는 경우 허가되지 않은 자원이 사용되지 않도록 해야 한다. ㅇ 접근 허가된 시스템자원이 식별되고, 사용가능한 기능과 매핑되어 있는지 확인 ㅇ 시스템자원 접근 이력이 로그로 기록되도록 설계되어 있는지 확인 ㅇ 조작된 입력으로 비허가 자원의 접근가능 여부를 점검할 수 있는 테스트 계획의 수립 여부 확인 → 경로조작에 사용 가능한 문자: / \\ ② 서버프로그램 안에서 셸을 생성하여 명령어를 실행해야 하는 경우 외부입력값에 의해 악의적인 명령어가 실행되지 않도록 해야 한다. ㅇ 명령어 실행 기능 포함여부를 식별, 해당 기능의 실행 가능 명령어 파라미터의 제한 설계 여부 확인 ㅇ 운영체제 명령어 실행 이력이 로그로 기록되도록 설계되어 있는지 확인 → 누가/언제/어느IP에서 썼는지 확인 가능하도록 설계되어야함. ㅇ 조작된 입력으로 비허가 명령어의 실행 가능 여부 테스트 계획 수립 여부 확인 → 검증해야 할 특수문자: | & ; |
보안대책 (구현단계) |
외부의 입력을 자원(파일, 소켓의 포트 등)의 식별자로 사용하는 경우, 적절한 검증을 거치도록 하거나, 사전에 정의된 적합한 리스트에서 선택되도록 한다. 특히, 외부의 입력이 파일명인 경우에는 경로 순회(directory traversal)14) 공격의 위험이 있는 문자( “ / \ .. 등 )를 제거할 수 있는 필터를 이용한다. |
진단방법 (구현단계) |
[ 경로 조작 ] ① 파일객체를 사용하는지 확인하고 ② 해당 파일에 대한 접근이 외부에서 직접 접근하는지 확인한다. 파일객체가 가리키는 경로에 외부 입력 값이 경로 순회 문자열에 대한 제거없이 사용되면 경로의 변경이 가능하게 되어 취약하다고 판정한다. [ 자원 삽입 ] ① 파일명, 소켓의 포트 등과 같은 자원을 사용하는지 확인하고 ② 해당 자원에 대한 접근이 외부에서 직접 접근하는지 확인한다 직접 접근하지 않고 매핑표나 리스트를 가질 경우엔 기본적으로 안전하다고 판단하고, 그 외의 경우엔 취약하다고 판정한다. |
다. 코드예제
ㅇ 분석
1:
2: String fileName = request.getParameter("P");
3: BufferedInputStream bis = null;
4: BufferedOutputStream bos = null;
5: FileInputStream fis = null;
6: try {
7: response.setHeader("Content-Disposition", "attachment;filename="+fileName+";");
8: ...
9:
10: fis = new FileInputStream("C:/datas/" + fileName);
11: bis = new BufferedInputStream(fis);
12: bos = new BufferedOutputStream(response.getOutputStream());
ㅇ 설명
1. r2에서 fileName값 받아 r10에서 그대로 사용함
2. ex) /admin 으로 입력해서 사용할 수 있음
ㅇ 수정
1:
2: String fileName = request.getParameter("P");
3: BufferedInputStream bis = null;
4: BufferedOutputStream bos = null;
5: FileInputStream fis = null;
6: try {
7: response.setHeader("Content-Disposition", "attachment;filename="+fileName+";");
8: ...
9: filename = filename.replaceAll("\\.","").replaceAll("/",""),replaceAll("\\\\","");
10: fis = new FileInputStream("C:/datas/" + fileName);
11: bis = new BufferedInputStream(fis);
12: bos = new BufferedOutputStream(response.getOutputStream());
ㅇ 설명
1. 경로순회가 가능한 문자열을 제거하는 방식을 사용함(아 근데 좀 방식이 맘에 안든다... normalize 사용하지...)
ㅇ 분석
1: public class ShowHelp {
2: private final static String safeDir = "c:\\help_files\\";
3: public static void main(String[] args) throws IOException {
4: String helpFile = args[0];
5: try (BufferedReader br = new BufferedReader(new FileReader(safeDir + helpFile))) {
6: String line;
7: while ((line = br.readLine()) != null) {
8: System.out.println(line);
9: }
10: ...
11: }
ㅇ 설명
1. r4에서 helpFile 입력값의 경로순회 문자열 검증 + 파일형식 검증이 안됨
2. 안된채로 r5에서 시스템에 접근함
ㅇ 수정
1: public class ShowHelp {
2: private final static String safeDir = "c:\\help_files\\";
3:
4: public static void main(String[] args) throws IOException {
5: String helpFile = args[0];
6: if (helpFile != null) {
7: helpFile = helpFile.replaceAll("\\.{2,}[/\\\\]", "");
8: }
9: try (BufferedReader br = new BufferedReader(new FileReader(safeDir + helpFile))) {
10: ...
ㅇ 설명
1. r7의 \\.{2,} 과 [/\\\\]이 의미하는 것: ①../ 나 ..\ 문자열 찾아내기, ② \나 / 찾기
2. 찾아서 없앰
ㅇ 분석
1:
2: string file = Request.QueryString["path"];
3: if (file != null)
4: {
5: File.Delete(file);
6: }
ㅇ 설명
1. path값에 대한 경로순회 문자열 검증 없이 file을 제거함
ㅇ 수정
1:
2: string file = Request.QueryString["path"];
3: if (file != null)
{
if(file.IndexOf('\\') != -1 || file.IndexOf('/') != -1)
{
Response.Write("P.T.A");
}
else
4: {
5: File.Delete(file);
6: }
ㅇ 설명
1. IndexOf를 사용해서 경로 순회 문자열 포함 여부를 확인함
(IndexOf 함수는 () 내 포함된 인덱스가 있는 경우 위치를 반환하며 없는 경우 -1을 반환함)
ㅇ 분석
1: char* filename = getenv(“reportfile”);
2: FILE *fin = NULL;
3:
4: fin = fopen(filename, “r”);
5: while (fgets(buf, BUF_LEN, fin)) {
6:
7: }
ㅇ 설명
1. r1에서 받은거 검증 안하고
2. r4에서 파일을 여네요
ㅇ 수정
1: FILE *fin = NULL;
2: regex_t regex;
3: Int ret;
4: char* filename = getenv(“reportfile”);
5: ret = regcomp(®ex, “.*\\.\\..*”, 0);
6:
7: ret = regexec(®ex, filename, 0, NULL, 0);
8: If (!ret) {
9:
10: }
11:
12: fin = fopen(filename, “r”);
13: while (fgets(buf, BUF_LEN, fin)) {
15: }
ㅇ 설명
1. r5, r7에서 경로조작 문자열 필터 설정,
2. r8에서 필터링
3. r12에서 파일 읽기
끝.