본문 바로가기
소프트웨어(SW) 보안약점 진단원/구현단계 보안약점 제거 기준

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

by 브루노W 2025. 6. 4.
유형 보안기능
보안약점 무결성 검사 없는 코드 다운로드
개요
원격으로부터 소스코드 또는 실행파일을 무결성 검사 없이 다운로드 받고, 이를 실행하는 제품들이 종종 존재한다.
 
이는 호스트 서버의 변조, DNS 스푸핑 (Spoofing) 또는 전송 시의 코드 변조 등의 방법을 이용하여 공격자가 악의적인 코드를 실행할 수 있도록 한다.
보안대책
DNS 스푸핑(Spoofing)을 방어할 수 있는 DNS lookup을 수행하고 코드 전송 시 신뢰할 수 있는 암호 기법을 이용하여 코드를 암호화한다.
 
또한 다운로드한 코드는 작업 수행을 위해 필요한 최소한의 권한으로 실행하도록 한다.
진단방법 클래스를 로드하기 위한 코드를 확인하고 클래스를 로드하기 전에 체크섬을 실행 및 비교하여 로드하려는 코드가 변조되지 않았음을 확인하다.
연관된 설계단계 기준 업로드/다운로드 파일검증

 

코드예제

 

● 안전하지 않은 코드 예 (Java)

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

URLClassLoader()로 원격에서 파일을 다운로드한 뒤 로드하면서, 대상 파일에 대한 무결성 검사를 수행하지 않아 파일변조 등으로 인한 피해가 발생할 수 있는 경우이다. 이러한 경우 공격자는 악의적인 실행코드로 클래스의 내용을 수정할 수 있다.

 

● 안전한 코드 예 (Java)

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

클래스를 로드하기 전 클래스의 체크섬(Checksum)을 실행하여 로드하는 코드가 변조되지 않았음을 확인한다.

 

 

● 안전하지 않은 코드 예 (C#)

public override bool DownloadFile() {
    var url = "https://www.somewhere.untrusted.com";
    var desDir = "D:/DestinationPath";
    string fileName = Path.GetFileName(url);
    string descFilePath = Path.Combine(desDir, fileName);
    try {
    	WebRequest myre = WebRequest.Create(url);
    } catch (Exception ex) {
    	throw new Exception(ex.Message);
    }
    try {
        byte[] fileData;
        // 파일 무결성 검사 없이 다운로드
        using (WebClient client = new WebClient()) {
        	fileData = client.DownloadData(url);
        }
        using (FileStream fs = new FileStream(descFilePath, FileMode.OpenOrCreate)) {
        	fs.Write(fileData, 0, fileData.Length);
        }
        return true;
    } catch (Exception ex) {
    	throw new Exception(ex.Message);
    }
}

파일 무결성 검사를 하지 않고 파일을 다운로드하고 있다.

 

● 안전한 코드 예 (C#)

public override bool DownloadFile() {
    var url = "https://www.somewhere.untrusted.com";
    var desDir = "D:/DestinationPath";
    string fileName = Path.GetFileName(url);
    string descFilePath = Path.Combine(desDir, fileName);
    try {
    	WebRequest myre = WebRequest.Create(url);
    } catch (Exception ex) {
    	throw new Exception(ex.Message);
    }
    try {
        byte[] fileData;
        using (WebClient client = new WebClient()) {
        	fileData = client.DownloadData(url);
        }
        // 해시값 등을 사용하여 다운로드 받은 파일 무결성 검사
        CheckIntegrity(fileData);
        using (FileStream fs = new FileStream(descFilePath, FileMode.OpenOrCreate)) {
        	fs.Write(fileData, 0, fileData.Length);
        }
        return true;
    } catch (Exception ex) {
    	throw new Exception(ex.Message);
    }
}

해시값 등을 이용하여 파일 무결성 검사 후 다운로드를 하고 있다.

 

 

● 안전하지 않은 코드 예 (C)

void foo(){
    /* ... */
    hFile = CreateFile((LPCWSTR)data,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    InternetQueryDataAvailable(m_hURL, &dwSize,0,0);
    InternetReadFile(m_hURL, lpBuffer, dwSize, &dwRead);
    WriteFile(hFile, lpBuffer, dwRead, &dwWritten, NULL);
    /* ... */
}

리턴값을 이용하여 무결성 검사를 하지 않고 있다.

 

● 안전한 코드 예 (C)

void foo(){
    /* ... */
    hFile = CreateFile((LPCWSTR)data,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    InternetQueryDataAvailable(m_hURL, &dwSize,0,0);
    bool result = InternetReadFile(m_hURL, lpBuffer, dwSize, &dwRead);
    if( result == true){
    	WriteFile(hFile, lpBuffer, dwRead, &dwWritten, NULL);
    }
    /* ... */
}

 

 

진단방법
 
클래스를 로드하기 위한 코드를 확인(①)하고 클래스를 로드하기 전에 체크섬을 실행 및 비교하여 로드하려는 코드가 변조되지 않았음을 확인하다.

 

 

● 일반적인 진단의 예

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

 


● 정탐코드

public void daoTest() throws Exception {
    // db sample : 84d5d0a08a3ec5e2d91a
    // 암호화 전, 후 : 1365ADMIN_01, aa84c40031d808196537ad3dcf81f9af
    String pwd= "46c165a343fd6841273ae04655af24dd";
    String pwd1= ARIAEngine.decARIA(pwd);
    System.out.println(pwd1);
}