article thumbnail image
Published 2023. 1. 31. 01:19

Prepend Characters

위에서 실험하여 확인한 @_> 문자열이 무시되는 이유에 대하여 설명하겠습니다. 또한 해당 원리를 파악 후 우리가 원하는 문자열을 기존 문자열 앞에 추가하는 방법을 알아보도록 하겠습니다.

Unicode Encoding

Unicode에선 인코딩 방식을 알아내기 위해 문자열 앞에 BOM이라는 것을 이용하여 파악합니다.

BOM은 문서(문자열) 앞에 눈에 보이지 않은 특정 바이트를 삽입하여 인코딩되었을 때, 해당 삽입한 바이트의 값을 보고 인코딩 방식을 파악할 수 있습니다.

 

The Unicode Standard and ISO 10646 define the character "ZERO WIDTH NON-BREAKING SPACE" (0xFEFF), which is also known informally as "BYTE ORDER MARK" (abbreviated "BOM").This usage, suggested by Unicode  and ISO 10646 Annex F (informative), is to prepend a 0xFEFF character  to a stream of Unicode characters as a "signature"; a receiver of such  a serialized stream may then use the initial character both as a hint  that the stream consists of Unicode characters and as a way to recognize the serialization order.

In serialized UTF-16 prepended with such a signature, the order is big-endian if the first two octets are 0xFE followed by 0xFF; if they are 0xFF followed by 0xFE, the order is little-endian. Note that 0xFFFE is not a Unicode character, precisely to preserve the usefulness of 0xFEFF as a byte-order mark.

 

RFC에서 확인한 것처럼 특정 인코딩에서 BOM을 사용합니다. 이는 디코딩 시 무시가 된다고 설명되어있습니다. 실험에서 사용된 @_> 문자열은 BOM으로 인식되기 때문에 무시되어, base64 문자열만 출력이 되었던 겁니다.

 

참고

https://www.rfc-editor.org/rfc/rfc2781#section-3.2

https://brownbears.tistory.com/124

Korean Character encoding for Internet Messages

인터넷 한글 메일의 전송을 위한 Encoding 규칙을 정의하기 위해서 1993년 국내에서 ISO-2022-KR을 개발하였습니다.

 

It is assumed that the starting code of the message is ASCII.  ASCII and Korean characters can be distinguished by use of the shift function.  For example, the code SO will alert us that the upcoming bytes will be a Korean character as defined in KSC 5601.  To return to ASCII the SI code is used.

 

Therefore, the escape sequence, shift function and character set used in a message are as follows:

SO KSC 5601
SI ASCII
ESC$)C Appears once in the beginning of a line before any appearance of SO characters.

 

문서에서 나온 것처럼 ISO-2022-KR로 구별될려면 ESC$)C로 시작해야합니다.

ISO-2022-KR 인코딩은 ISO-2022-CN, ISO-2022-CN-EXT, ISO-2022-JP, ISO-2022-JP-2와 함께 7 Bit로 사용되는 ISO-2022 코드 버전 중 하나입니다. 그러나 ISO-2022-KR은 iconv 함수에서 사용 가능한 유일한 인코딩입니다. (국뽕…?)

 

PHP 코드를 통해 확인한 결과 ISO-2022-KR에서만 BOM이 붙어지는 것을 확인할 수 있었습니다.

0x1b 0x24 0x29 0x43 => 0x1b $ ) C

 

참고

https://www.rfc-editor.org/rfc/rfc1557.html

https://koreascience.kr/article/JAKO199811919559785.pdf

 

문자열 앞에 문자를 추가할 수 있는 인코딩

Encoding identifier Prepended characters
ISO-2022-KR \x1b$)C
UTF-16 (Little Endian) \xFF\xFE
UTF-32 (Little Endian) \xFF\xFF\x00\x00

위 표는 각 인코딩에서 추가되는 BOM을 정리한 표입니다. 위 인코딩은 base64 문자열의 무결성을 손상시키지 않고 문자를 추가하여 PHP Filter Chain에서 사용할 수 있습니다.

 

원하는 문자열을 삽입하는 방법

위에서 설명하였던 것처럼 특정 인코딩을 통해 BOM이라는 것을 삽입할 수 있습니다.

우리는 이제 BOM이 아닌 우리가 원하는 문자열을 삽입하는 방법에 대하여 알아보도록 하겠습니다.

 

특정 문자를 얻기 위해서는 기존 인코딩에서 다른 인코딩으로 사용되는 것을 목표로 해야합니다.

예시로 8이라는 문자열을 얻고 싶으면, ISO-8859-10과 UTF-16이 필요로 합니다.

 

위와 같이 UTF-8 → UTF-16 → LATIN-6 → UTF-16으로 인코딩을 진행하면, UTF-16의 BOM(\xFF\xFE)이 LATIN-6으로 인코딩되면서 ĸþ으로 변경됩니다. 또 다시 UTF-16으로 인코딩하면, UTF-16의 BOM이 문자열 앞에 붙으면서 ĸþ을 문자열로 구분하여 UTF-16의 테이블에 정의된 ĸþ의 값 (\x01\x38\x00\xFE)으로 변경됩니다.

 

이 과정을 거친 문자열을 출력한다면, UTF-8로 출력이 되고, \x01\x38\x00\xFE 값 중 \x38은 UFT-8의 테이블에서 8으로 정의되었기 때문에 8이 출력됩니다.

 

PHP 코드로 확인한 결과, 마지막 출력 값에서 8 문자가 출력되는 것을 확인할 수 있었다.

 

참고

https://en.wikipedia.org/wiki/ISO/IEC_8859-10

https://www.fileformat.info/info/charset/UTF-16/list.htm

https://www.utf8-chartable.de/

원치 않는 결과

해당 트릭을 사용할 때 어떠한 어려움이 있는지 알아보도록 하겠습니다.

 

PHP Filter Chain이 발견된 후 base64 문자를 생성하려는 첫번째 시도는 PHP Filter를 이용한 LFI2RCE 트릭입니다. LFI2RCE 트릭은 파이썬으로 iconv에 존재하는 인코딩 방식들을 랜덤으로 무차별적으로 기입하여 문자열 앞에 추가되는 문자가 Base64의 64개의 문자에 속하는지 확인합니다.

 

이러한 방법으로 Base64의 64개의 문자가 생성되는 페이로드 목록이 생성되었지만, 전체 Chain에선 동작하지 않습니다. 동작하지 않은 이유를 설명하기 위해 임의의 문자열 앞에 b 문자를 붙이는 것을 예시로 보여드리겠습니다.

 

 

https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-php-filters#improvements

페이로드 목록에서 b 문자를 생성하기 위해 CP1399 코덱을 사용하였습니다. CP1399 코덱은 EBCDIC의 일본어 버전 중 하나의 이름입니다. (IBM-1027 코덱에 매우 근접)

 

하지만 EBCDIC과 ASCII 간에 호환성 문제가 있습니다.

위 사진에서 ASCII 코드인 0x41 (A)를 EBCDIC JP으로 인코딩 시 원래대로라면, 0x41은 A0으로 매칭이 되어야 합니다. 하지만, 0x41을 1A으로 매칭이 되는 현상이 있었습니다. (해당의 의견은 추측성이라서 검증이 필요합니다.)

 

위 코드에서 보는 것처럼 마지막 결과 값에서 문자 b가 추가되는 것을 확인할 수 있지만, START 문자열의 무결성은 깨졌습니다.

 

또한 ISO-2022-KR의 경우, PHP 필터 체인 기법을 토대로 공격 시 사용되는 Base64 인코딩에 의해서 ISO-2022-KR의 BOM인 \x1b$)C의 문자 C가 Base64의 64개의 문자 중 하나이기 때문에 C가 아닌 다른 것을 앞에 추가하면 PHP 필터 체인 기법이 이루어지지 않습니다.

 

이 밖에 IBM의 코덱이 다양하며, 각각의 코덱은 각자의 방식으로 동작합니다. 또한 UTF-8로 인코딩 시 테이블에 존재하지 않는 문자일 시 UTF-8의 테이블에서 가까운 문자로 인코딩이 되기 때문에 체인을 구축하는 데 많은 시간이 걸릴 수 있다는 문제점도 존재합니다.

 

참고

http://itdoc.hitachi.co.jp/manuals/3020/30203J3820/ISUS0268.HTM  → "(1) EBCDICコードからJISコードへの変換" 참고

https://www.fileformat.info/info/charset/UTF-32/list.htm

https://www.asciitable.com/

복사했습니다!