반응형

0. 목적

 - python의 pickle 모듈에서 발생되는 deserialize 취약점에 대해서 분석




1. 정의

1.1 pickle 모듈

python의 pickle 모듈은 객체 구조의 직렬화와 역 직렬화를 위한 바이너리 프로토콜을 구현하기 위해 사용한다. 


그렇다면 직렬화와 역 직렬화는 무엇을 의미하는 것일까? 이에 대한 것을 간단하게 표현하자면 다음과 같다.    

 - pickling ( = Serialization ) : 파이썬 객체 계층 구조 → 바이트 스트림

 - unpickling ( = Deserialization ) : 바이트 스트림 → 파이썬 객체 계층 구조

바로 파이썬 객체 계층 구조를 바이트 스트림으로 변환하는 것이 직렬화 혹은 pickling이라 하고, 이에 대한 역 연산이 바로 역 직렬화 혹은 unpickling인 것이다.

     

그렇다면 이러한 직렬화를 하는 이유는 무엇일까?

그 이유는 직렬화된 데이터를 파일/데이터베이스에 저장하거나 세션에 걸쳐 프로그램 상태를 유지하거나, 네트워크를 통해 데이터를 전송하기 위해서이다. pickle된 바이트 스트림은 unpickling을 통해 원래 객체 계층을 다시 만드는데 사용할 수 있다.



1.2 모듈 인터페이스

pickle 모듈의 인터페이스 중 대표되는 4개의 인터페이스에 대해서 설명하겠다.


1) pickle.dump : 객체 obj의 피클 된 표현을 열린 파일 객체 file에 쓴다.

2) pickle.dumps :  객체 obj의 피클 된 표현을 파일에 쓰는 대신 bytes 객체로 반환한다.


3) pickle.load : 열린 파일 객체 file에서 객체의 피클 된 표현을 읽고, 그 안에 지정된 객체 계층 구조를 재구성하여 반환

4) pickle.loads : 객체의 피클 된 표현 data의 재구성된 객체 계층 구조를 반환


아래는 pickle.dump을 이용하여 "Rootable"이라는 String 객체를 직렬화하고, pickle.loads를 이용하여 다시 역직렬화 예시이다.

python3에서 TEST한 코드



1.3 pickle 동작 분석

- pickletools.dis를 통해 disassemble 한 것을 분석하여 pickle이 어떻게 동작하는지를 분석해보자.

- 아래 예제는 info라는 딕셔너리에 name, userid, password라는 3개의 쌍을 추가하고 이를 직렬화(pickling)한 뒤 이를 pickletools.dis한 것이다.


각각의 문장에 대해 간단히 설명을 추가해보자면 다음과 같다.

 0: PROTO    3        → protocol의 버전을 나타내는 것으로 python의 버전이라고 생각하면 된다.

 2: EMPTY_DICT    → 빈 Python list를 만들고 이를 stack에 올린다.

 3: BINPUT  [index]      → 이전의 item(빈 리스트)를 스택의 0번째에 올린다. 뒤의 숫자는 index를 나타낸다고 생각하면 된다.

 5: MARK            → 스택에서 리스트의 시작을 의미한다.

 6: BINUNICODE '[문자열]' → binary로 표현된 것을 unicode로 변환하여 스택에 넣는다.

 78 : SETITEMS (MARK at 5) → 5번째에 있는 MARK를 기준으로 stack에 있는 key와 value를 list에 넣는다.



(참고)pickle.py에 SETITEMS는 다음과 같이 구현이 되어있다.

1
2
3
4
5
def load_setitems(self):
    items = self.pop_mark()
    dict = self.stack[-1]
    for i in range(0len(items), 2):
        dict[items[i]] = items[i + 1]
cs





2. deserialize 취약점 동작 원리

- pickle 모듈은 다양한 메소드를 지원하고 있다. 이 중 object.__reduce__() 메소드에서 취약점이 발생한다. 


2.1 __reduce__() 메소드

__reduce__() 메소드는 파이썬 객체 계층 구조를 unpickling 할 때 객체를 재구성하는 것에 대한 tuple을 반환해주는 메소드이다.


이렇게 말을 한다면 무슨 말인지 이해가 잘 안가니 조금 더 풀어서 설명을 하겠다.


바이트 스트림을 unpickle할 때, pickle 모듈은 먼저 original object의 인스턴스를 만들고 나서 그 인스턴스를 올바른 데이터로 채운다. 이를 위해서 바이트 스트림에는 original object 인스턴스에 특정된 데이터만 포함한다. 

그러나 데이터만을 가지고 있는 것으로는 충분하지 않을 수 있다. object를 성공적으로 unpickle하기 위해, 그 pickle된 바이트 스트림에는 unpicker에 대한 명령 피연산자와 함께 원래 객체 구조를 재구성하는 명령이 포함되어 있어 객체 구조를 채울 수 있다.


여기서 unpicker에 대한 명령 피연산자와 원래 객체 구조를 재구성하는 명령을 __reduce__() 메소드를 통해 선언하는 것이다. 이를 통해 object가 unpickle될 때 어떻게 재구성될지를 알려주는 것이다.


__reduce__ 메소드는 보통 리턴 값은 2개의 인자를 가지고 있으며 다음의 구성을 가지고 있다.

⦁ 호출가능한 객체 (보통 호출할 클래스의 이름이다)

⦁ 호출가능한 객체에 대한 인자. 호출가능한 객체가 인자를 받아들이지 않으면 빈 튜플을 제공해야 한다.


이 때 호출가능한 객체에 eval 혹은 os와 같이 명령어를 실행할 수 있는 클래스를 임의로 지정할 수 있다면, 이로 인해 RCE와 같은 보안 취약점이 발생할 수 있다.



2.2 취약점 예시

 - 아래 예시는 eval 함수를 이용하여 rootable이라는 문자열을 print 하는 예시이다. 아래와 같이 해당 payload를 loads하게 되면 print 함수가 실행되는 것을 볼 수 있다.

 - stack에 함수로 [ eval ]을 입력하고, 그 인자로 [ print('rootable') ]를 입력해서 실행되는 것을 알 수 있다.



 - payload는 __builtin__.eval(print('rootable'))를 의미함.


 - b : byte 형식임을 표현

 - c : 모듈의 이름 앞에 붙는 문자

 - ( : stack 의 시작지점

 - S : 문자열 표현

 - tR : 코드의 끝에 붙는 문자



 - Exploit1 클래스를 보면 print(123) 문장을 실행시키는 exploit 코드이다. 

eval은 식을 실행 시키는 것이고 exec는 문장을 실행시키는 것이기 때문에 print(123)을 실행시키기 위해서는 exec를 이용해야 한다. 하지만, exec는 return 값 자체가 없기 때문에 compile 함수를 통해 컴파일 코드로 변환시킨 후 eval로 실행시켜주는 것으로 exploit할 수 있다.


 - Exploit2 클래스를 보면 os 모듈을 이용하여 whoami라는 명령어를 실행시킨 exploit코드이다.

해당 exploit을 활용하여 RCE가 가능하다.


./flag.txt 파일 읽기


class Exploit(object):

   def __reduce__(self):

      p = "open('./flag.txt').read()"

      return (eval,(p,))



(참고)

https://stackoverflow.com/questions/19855156/whats-the-exact-usage-of-reduce-in-pickler

https://rushter.com/blog/pickle-serialization-internals/

https://as3617.tistory.com/34?category=866748

https://whitesnake1004.tistory.com/704

https://www.synopsys.com/blogs/software-security/python-pickling/

반응형

'Hacking > Web' 카테고리의 다른 글

XSS In event handler  (0) 2020.08.06
Content Security Policy(csp)  (2) 2020.07.28
Filter bypass Using Multipart form data  (0) 2020.05.08
SQL Injection where filter bypass  (2) 2020.04.23
get column name in mysql error based sql injection  (0) 2020.04.22
블로그 이미지

rootable

,
반응형

만약 XSS(Cross Site Scripting) 취약점이 발견되어 대응을 해야할 때 각 파라미터별로 원하는 문자를 모두 필터링 걸어준다는 것은 비효율적이라는 생각이 들었다.


그래서 XSS 방어를 위한 관련 라이브러리가 존재하지 않을까하여 찾아본 것을 공유한다.


1. StringEscapeUtils

 StringEscapeUtils 클래스는 Apache에서 공식으로 제공해주는 class이다. 해당 클래스에 대한 설명은 다음과 같이 나와있다.

Escape and unescape Strings for Java, Java Script, HTML and XML

 

설명에서 보는 것처럼 해당 클래스는 HTML 뿐만 아니라 Java, Java Script, XML에 대한 escape와 unescape도 제공을 해주고 있다.

하지만 이번 포스팅의 주제가 XSS인 만큼 HTML에 대한 기능에 대해서만 살펴보도록 하겠다.


 ◎ escapeHtml3(String input) : HTML entites를 사용하여 String 안의 문자들을 escape한다. HTML version 3.0을 escape하는 translator object이다.


 ◎ escapeHtml4(String input) : HTML entites를 사용하여 String 안의 문자들을 escape한다. HTML version 4.0을 escape하는 translator object이다.


 ◎ unescapeHtml3(String input) : entity escapes를 포함하는 문자열을 실제 유니코드 문자들로 entity escapes를 포함하는 문자열을 실제 유니코드 문자가 포함된 문자열로 unescape한다. HTML version 3.0을 unescape하는 translator object이다.


 ◎ unescapeHtml4(String input) : entity escapes를 포함하는 문자열을 실제 유니코드 문자들로 entity escapes를 포함하는 문자열을 실제 유니코드 문자가 포함된 문자열로 unescape한다. HTML version 4.0을 unescape하는 translator object이다.



간단하게 위의 class를 이용한 코드는 다음과 같다. 


import org.apache.common.lang3.StringEscapeUtils;

...

String text = request.getParameter("text");

text = StringEscapeUtils.escapeHtml4(text);

...


참고 : http://commons.apache.org/proper/commons-lang//apidocs/org/apache/commons/lang3/StringEscapeUtils.html





2. Lucy-XSS

Lucy-XSS는 naver에서 open source 형태로 제공해주는 XSS 관련 라이브러리이다.

해당 라이브러리에 대한 설명을 보면 이 library 또한 위에서 말한 StringEscapeUtils를 사용했음을 알 수 있다.


Lucy-XSS에는 XssFilter, XssPreventer 두가지가 제공되는데 각각은 다음과 같을 때 사용하라고 명시되어 있다.


XssPreventer : HTML 이외의 간단한 text 파라미터일 때에는 XssPreventer를 사용

XssFilter : 메일, 게시판 등 입력값으로 HTML 태그를 받을 경우에는 XssFilter를 사용


아래는 각각에 대한 간단한 코드이다.


XssPreventer

@Test

public void testXssPreventer() {

String dirty = "\"><script>alert('xss');</script>";

String clean = XssPreventer.escape(dirty);


assertEquals(clean, "&quot;&gt;&lt;script&gt;alert(&#39xss&#39);&lt;/script&gt;");

assertEquals(dirty, XssPreventer.unescape(clean));

}


◎ XssFilter

@Test

public void testSuperSetFix() {

XssSaxFilter filter = XssSaxFilter.getInstance("lucy-xss-superset-sax.xml");

String expected = "<TABLE class=\"Naver_Layout_Main\" style=\"TABLE-LAYOUT: fixed\" cellSpacing=\"0\" cellPadding=\"0\" width=\"743\">" + "</TABLE>" + "<SPAN style=\"COLOR: #66cc99\"></SPAN>";

String actual = filter.doFilter(clean);

assertEquals(expected, actual);

}


참고 : https://github.com/naver/lucy-xss-filter

반응형

'Develope > JSP' 카테고리의 다른 글

[JSP 보안] Secure Coding  (0) 2018.05.01
[JSP] kali linux에 JSP 개발환경 세팅  (0) 2018.03.31
블로그 이미지

rootable

,
반응형

1. 1부터 10 사이의 임의의 수

Math 클래스의 random() 을 사용하여 1과 10 사이의 임의의 수를 구하는 코드는 다음과 같다.


1
int score = (int)(Math.random() * 10)+1;
cs


이제 위의 코드를 조금 더 풀어서 설명해보도록 하겠다.

우선 Math 클래스의 random() 함수는 0.0과 1.0 사이의 범위에 속하는 하나의 double 값을 반환한다.


0.0 <= Math.random() < 1.0


여기에 10을 각각 곱하면 다음과 같다.


0.0 * 10 <= Math.random() * 10 < 1.0 * 10

0.0 <= Math.random() * 10 < 10.0


이것을 int형으로 변환시켜주면 다음과 같다.


0 <= (int)(Math.random() * 10) < 10


여기에 1을 더해주면 다음과 같다.

1 이상 11 미만이므로 정수로 1과 10 사이의 정수가 출력된다.


0+1 <= (int)(Math.random() * 10)+1 < 10+1

1 <= (int)(Math.random() * 10)+1 < 11




2. 임의의 주사위 수

위의 코드를 이용하여 임의의 주사위 숫자를 구하는 것도 다음과 같이 할 수 있다.

주사위는 1부터 6까지의 수이므로 다음과 같은 코드로 구현이 가능하다.

1
int player = (int)(Math.random() * 6)+1;
cs



3. 임의의 문자

임의의 문자가 ascii 코드 상에서 연속적으로 존재한다는 특징을 이용한 것이다.

만약 영문의 대문자 중 한 문자를 얻고자 한다면 다음과 같은 코드로 구현할 수 있다.

1
char Upper = (char)(Math.random()*26+65);
cs


간단하게 설명하자면 영문자가 총 26개이므로 26을 곱해준 것이고 대문자 ascii 코드가 65부터 시작하기 때문에 65를 더해준다.


여기서 위와 다른 점은 char형으로 형변환하기 전에 +65를 해주었다는 점이다.

만약 위와 같이 (char)(Math.random()*26)+65; 를 하였다면 +65를 할 때 int형으로 변환되므로 에러가 발생된다.



반응형
블로그 이미지

rootable

,

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

반응형
1. 소문자 → 대문자 변환
소문자를 대문자로 변경하는 코드이다.

Ascii 코드 상으로 대문자가 소문자보다 32만큼 작다는 것을 이용한 코드이다.


1
2
3
4
5
6
7
class LowerToUpper {
    public static void main(String[] args){
        char lowerCase = 'a';        
        char upperCase = (char)(lowerCase - 32);
        System.out.println(upperCase);
    }
}
cs


lowerCase 변수에 대문자로 변환하고자 하는 문자를 넣어주면 된다.

이 코드를 뼈대로 원하는 것을 더 추가하면 된다.




2. 대문자 → 소문자 변환

대문자를 소문자로 변경하는 코드이다.

위의 1번에서와 반대로 소문자는 대문자보다 32만큼 크다는 것을 이용한 코드이다.


1
2
3
4
5
6
7
class UpperToLower {
    public static void main(String[] args){
        char upperCase = 'A';        
        char lowerCase = (char)(upperCase + 32);
        System.out.println(lowerCase);
    }
}
cs



위의 두 코드를 뼈대로 원하는 것을 추가하는 방식으로 활용하길 바란다.

반응형
블로그 이미지

rootable

,

터널링의 종류

2020. 7. 12. 20:17

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

APT 침투

2020. 7. 12. 20:10

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.