자격/SW보안약점진단원

구현단계 보안약점 제거 기준-무결성 검사 없는 코드 다운로드

Ignatius Heo 2023. 7. 6. 13:40

작성일: 230706

 

※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.

 

정리 내용: 소프트웨어 보안약점 진단 가이드(394~399p)

구분 - 입력데이터 검증 및 표현
설계단계 - 업로드·다운로드 파일 검증
https://cryptocurrencyclub.tistory.com/98
개요 원격으로부터 소스코드 또는 실행파일을 무결성 검사 없이 다운로드 받고, 이를 실행하는 제품들이 종종 존재한다.
이는 호스트 서버의 변조, DNS 스푸핑 (Spoofing) 또는 전송시의 코드 변조 등의 방법을 이용하여 공격자가 악의적인 코드를 실행할 수 있도록 한다.
진단 세부사항
(설계단계)
① 업로드되어 저장되는 파일의 타입, 크기, 개수, 실행권한을 제한해야 한다. 업로드되는 파일의 타입, 크기, 개수 및 실행권한을 제한하도록 설계하고 있는지 확인한다.
  ㅇ  업로드 가능한 파일의 타입, 크기, 개수와 저장 시 파일의 퍼미션을 정의하고 있는지 확인(업로드 기능을 지원하는 프레임워크, 라이브러리 사용 여부 확인 후 파일 속성 제한 설정 여부 확인)
  ㅇ  허용되지 않는 파일 타입, 크기, 개수 업로드의 제한 기능 테스트 계획 수립 여부 확인
  ㅇ  업로드 후 서버에 저장된 파일의 실행 제한 여부 점검 테스트 계획 수립 여부 확인
 
 ② 업로드되어 저장되는 파일은 외부에서 식별되지 않아야 한다. 업로드된 파일을 외부에서 식별되거나 직접 접근할 수 없도록 설계하고 있는지 확인한다.
  ㅇ  저장 경로와 파일명을 외부에서 알 수 없도록 정의하는지 확인(난수 + 중복 안되도록)
  ㅇ  원본 파일명과 저장경로/파일명의 매핑정보를 관리하도록 설계되어 있는지 확인
  ㅇ  저장된 파일명, 경로가 외부에서 식별 가능한지 점검할 수 있는 테스트 계획 수립 여부 확인
 
 ③ 파일 다운로드 요청 시, 요청파일명에 대한 검증작업을 수행해야 한다. 파일 다운로드 요청 시 파일에 대한 검증작업이 이루어지도록 설계하고 있는지 확인한다.
  ㅇ  파일 다운로드 요청방식의 안전한 설계 여부 확인(경로 조작 문자 포함 여부, DB정보를 이용하여 요청파일의 유효성 점검 여부)
  ㅇ  조작된 파일명을 이용한 파일 다운로드 요청의 차단 여부 확인
 
 ④ 다운로드받은 소스코드나 실행파일은 무결성 검사를 실행해야 한다. 다운로드받은 소스코드와 실행파일에 대해 무결성을 검증하도록 설계하고 있는지 확인한다.
  ㅇ  파일의 무결성 검사를 위한 값(예: 해시)의 제공 여부 확인
  ㅇ  무결성 검사 수행 후 파일 다운로드하도록 절차가 설계되어 있는지 확인
  ㅇ  변조된 파일에 대한 다운로드 처리가 차단되는지 점검할 수 있는 테스트 계획의 수립 여부 확인
보안대책
(구현단계)
DNS 스푸핑(Spoofing)을 방어할 수 있는 DNS lookup을 수행하고 코드 전송시 신뢰할 수 있는 암호 기법을 이용하여 코드를 암호화한다.
또한 다운로드한 코드는 작업 수행을 위해 필요한 최소한의 권한으로 실행하도록 한다.
진단방법
(구현단계)
① 클래스를 로드하기 위한 코드를 확인하고

클래스를 로드하기 전에 체크섬을 실행 및 비교하여 로드하려는 코드가 변조되지 않았음을 확인하다.

 

다. 코드예제

 

ㅇ 분석

1: URL[] classURLs = new URL[] { new URL("file:subdir/") };
2: URLClassLoader loader = new URLClassLoader(classURLs);
3: Class loadedClass = Class.forName("LoadMe", true, loader);

ㅇ 내용

1. 클래스 로드 후 무결성 검사 없이 사용함

 

ㅇ 수정

// 공개키 방식의 암호화 알고리즘과 메커니즘을 이용하여 전송파일에 대한 시그니처를 생성하고 파일의
변조유무를 판단한다. 서버에서는 Private Key를 가지고 MyClass를 암호화한다.
1: String jarFile = "./download/util.jar";
2: byte[] loadFile = FileManager.getBytes(jarFile);
3: loadFile = encrypt(loadFile, privateKey);
// jarFileName으로 암호화된 파일을 생성한다.
4: FileManager.createFile(loadFile, jarFileName);
// 클라이언트에서는 파일을 다운로드 받을 경우 Public Key로 복호화한다.
5: URL[] classURLs = new URL[] { new URL("http://filesave.com/download/util.jar") };
6: URLConnection conn = classURLs.openConnection();
7: InputStream is = conn.getInputStream();
// 입력 스트림을 jarFile명으로 파일을 출력한다.
8: FileOutputStream fos = new FileOutputStream(new File(jarFile));
9: While (is.read(buf) != -1) {
10: ......
11:}
12:byte[] loadFile = FileManager.getBytes(jarFile);
13:loadFile = decrypt(loadFile, publicKey);
// 복호화된 파일을 생성한다.
14:FileManager.createFile(loadFile, jarFile);
15:URLClassLoader loader = new URLClassLoader(classURLs);
16:Class loadedClass = Class.forName("MyClass", true, loader);

ㅇ 내용

1.  이건 파일저장소에서부터 보내려는 파일을 암호화 후 수신단에서 복호화해서 사용하는 코드임. 앞에 파일 암호화단을 추가함

 


ㅇ 분석

1: public override bool DownloadFile()
2: {
3: var url = "https://www.somewhere.untrusted.com";
4: var desDir = "D:/DestinationPath";
5: string fileName = Path.GetFileName(url);
6: string descFilePath = Path.Combine(desDir, fileName);
7: try
8: {
9: WebRequest myre = WebRequest.Create(url);
10: }
11: catch (Exception ex)
12: {
13: throw new Exception(ex.Message);
14: }
15: try
16: {
17: byte[] fileData;
//파일 무결성 검사 없이 다운로드
18: using (WebClient client = new WebClient())
19: {
20: fileData = client.DownloadData(url);
21: }
22: using (FileStream fs = new FileStream(descFilePath,
FileMode.OpenOrCreate))
23: {
24: fs.Write(fileData, 0, fileData.Length);
25: }
26: return true;
27: }
28: catch (Exception ex)
29: {
30: throw new Exception(ex.Message);
31: }
32: }

ㅇ 내용

1. r20에서 무결성 검사 안하고 다운로드

 

ㅇ 수정

1: public override bool DownloadFile()
2: {
3: var url = "https://www.somewhere.untrusted.com";
4: var desDir = "D:/DestinationPath";
5: string fileName = Path.GetFileName(url);
6: string descFilePath = Path.Combine(desDir, fileName);
7: try
8: {
9: WebRequest myre = WebRequest.Create(url);
10: }
11: catch (Exception ex)
12: {
13: throw new Exception(ex.Message);
14: }
15: try
16: {
17: byte[] fileData;
18: using (WebClient client = new WebClient())
19: {
20: fileData = client.DownloadData(url);
21: }
//해시 값 등을 사용하여 다운로드 받은 파일 무결성 검사
22: CheckIntegrity(fileData);
23: using (FileStream fs = new FileStream(descFilePath,
FileMode.OpenOrCreate))
24: {
25: fs.Write(fileData, 0, fileData.Length);
26: }
27: return true;
28: }
29: catch (Exception ex)
30: {
31: throw new Exception(ex.Message);
32: }
33: }

ㅇ 내용

1. 근데 이 코드에서는 다운로드 후 무결성 검사를 수행하는데 무결성 검사를 어느 방식으로 수행하는지 안나옴

 


ㅇ 분석

1: void foo(){
2: /* ... */
3: hFile = CreateFile((LPCWSTR)data,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
4: InternetQueryDataAvailable(m_hURL, &dwSize,0,0);
5: InternetReadFile(m_hURL, lpBuffer, dwSize, &dwRead);
6: WriteFile(hFile, lpBuffer, dwRead, &dwWritten, NULL);
7: /* ... */

ㅇ 내용

1. CreateFile로 버퍼를 열고, 5에서 링크 정보를 받아 6에서 다운로드 하는 순서로 보임. 그럼 4~5에 사이에 검증하는 코드가 있어야 할듯함

 

ㅇ 수정

1: void foo(){
2: /* ... */
3: hFile = CreateFile((LPCWSTR)data,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
4: InternetQueryDataAvailable(m_hURL, &dwSize,0,0);
5: bool result = InternetReadFile(m_hURL, lpBuffer, dwSize, &dwRead);
6: if( result == true){
7: WriteFile(hFile, lpBuffer, dwRead, &dwWritten, NULL);
8: }
9: /* ... */
10:}

ㅇ 내용

 

 

 

 

 

끝.