class 동적로딩
보안 전무업체에서 사용하는 소스 취약점 점검툴로 운영중인(또는 개발중인) 소스를 점검해 보면 동적으로 Class를 로딩하는 부분이 취약 하다는
보고서를 받게 되는 경우가 종종있습니다.
이때 보고서에 적힌 취약점 명은 다음과 같을 경우가 있습니다.
국정원 취약점 : 2. 파일 다운로드 취약점
안행부 보안취약점 : 15. 무결성 검사없는 코드 다운로드 취약점
그리고 취약하다고 보고된 코드는 보통 다음과 같을수 있습니다.
String className = "sample.security.checksum.DesObject";
<<중략>>
Class loadedClass = Class.forName(className);
WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();
이에 대한 조치를 다음과 같이 수행하라고 적혀 있다.
Class loadedClass = Class.forName(className);
if(loadedClass.getChecksum() == loadedClass.isChecksum() ){
WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();
}
상기 취약점은 클래스가 동적으로 로딩되는 과정에서 다른 클래스로 대체되어 악성코드가 실행될 수 있다는 가정이 전제되어 있습니다.
따라서 점검툴은 소스상에 Class.forName() 코드가 보이면 무조건 취약 코드로 잡아 낸다. 이 과정에서 오탐도 많이 발생하는데, 예를 들어 동적으로 로딩되는 클래스가 단순히 WEB-INF>classes 밑에 놓여있는 클래스들일 경우에는 로딩 과정에서 다른 객체로 변경될 가능성이 거의 제로에 가까우므로 오탐으로 간주해도 되나, 원격지 서버에 있는 라이브러리들을 동적으로 로딩하는 경우에는 충분히 악성클래스로 대체 될 수 있으르모 가이드를 참고해서 적절한 조치를 취해야 합니다.
이때의 조치방법으로는 Object Checksum 이나 File Checksum 의 소스 코드를 소개합니다.
다음은 Object Checksum의 소스 코드입니다.
public static BigInterger getObjChecksum(Object obj) {
BigInteger bigInt;
MessageDigest mDigest = null;
if(obj == null) {
bigInt = BigInteger.ZERO;
}else{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
//직렬화된 객체를 ByteArrayOutputStream(baos)에 write한다.
oos.close();
mDigest = MessageDigest.getInstance("SHA-256");
//SHA-256 알고리즘을 사용한다.
mDigest.update(baos.toByteArray());
}catch (IOException e){
System.out.println(e.toString());
BigInteger.ZERO;
}catch (NoSuchAlgorithmException e) {
System.out.println(e.toString());
bigInt = BigInteger.ZERO;
}
}
bigInt = new BigInteger(1, mDigest.digest());
return bigInt;
}
이렇게 작성을 하게 되면 1233459834509830455656566456464564665456 와 같은 해쉬코드가 반환됩니다.
구글링을 해보면 유사한 코드가 소개되어 있는데 대부분 해쉬 알고리즘으로 MD5를 사용한 코드가 공개되어 있습니다. 그러나 MD5는 보안 취약점이 다수 발견되어 현재는 사용하지 않는 알고리즘이므로 보다 강화된 SHA-256 알고리즘을 사용한 코드로 변경하였습니다.
다음은 File Checksum 의 소스 코드입니다.
public static String getFileChecksm(String filename){
String checksum;
byte[] byteData = createChecksum(filename);
StringBuffer sb = new StringBuffer();
for(int i = 0 ; i < byteData.length ; i++) {
sb.append(Integer.toString((byteData[i]&0xff) + 0x100, 16).substring(1));
}
checksum = sb.toString();
return checksum;
}
public static byte[] createChecksum(String filename){
byte[] result = null;
InputStream fis;
try {
fis = new FileInputStream(filename);
byte[] buffer = new byte[1024];
MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
mDigest.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
result = mDigest.digest();
} catch (FileNotFoundException e) {
System.out.println(e.toString());
} catch (NoSuchAlgorithmException e) {
System.out.println(e.toString());
} catch (IOException e) {
System.out.println(e.toString());
}
return result;
}
//c0bbc7bcf23736a8560f68f7af73cd974f4a48ab7bb79cb05cedab5c4549990//와 같은 해쉬코드가 반환됩니다.
'JAVA' 카테고리의 다른 글
[JAVA] Class.forName 사용하기 (0) | 2017.10.30 |
---|---|
[JAVA] class 클래스 로딩 (0) | 2017.10.30 |
[JAVA] class path에서 resource 찾기 (0) | 2017.10.30 |
[JAVA] cache function (0) | 2017.10.27 |
[JAVA] BigInteger형을 바이트 배열로!! (0) | 2017.10.27 |