[SQLite fts3] fts3_tokenizer 취약점
두비니
·2021. 7. 27. 01:03
fts3_tokenizer
0. fts3_tokenizer에 대해서
일단 이 친구가 뭐하는 친구인지 봅시다. FTS란 Full Text Search의 줄임말로, 효율적으로 빠르게 문서(특히 대용량)을 검색하기 위해 구상된 Table입니다. 무식하게 찾으면 시간이 무진장 오래 걸리니깐 최대한 최적화시켜놓은 시스템이라고 보는게 가장 편할 것 같네요.
그래서 그 검색을 편하게 해주는 것 중 하나가 Tokenizers입니다. Tokenizer는 문장에서 단어를 어떤 방식으로 잘라낼지에 대한 방법을 지정합니다. 보통은 문장에서 공백(' ')을 기준으로 token을 만들겠지만 특정 언어에 맞추어진 custom tokenizer라고 생각하시면 좋을 것 같습니다.
기본적으로 제공하는 tokenizer는 다음과 같습니다.
- unicode61 (기본값)
- ascii
- porter
인자는 순서대로 이름, 두 번째부터는 tokenizer implementation에 이용되는 값들입니다. 사용 방법은 다음과 같습니다.
-- The following are all equivalent
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'porter ascii');
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = "porter ascii");
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = "'porter' 'ascii'");
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = '''porter'' ''ascii''');
-- But this will fail:
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = '"porter" "ascii"');
-- This will fail too:
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'porter' 'ascii');
출처 : https://www2.sqlite.org/fts3.html
그래서 이런식으로 전체 텍스트 인덱스를 사용하면 테이블에 여러 개의 큰 문서가 포함되어 있어도 하나 이상의 단어(토큰)가 포함 된 모든 행에 대해 쉽게 끌어올 수 있다보니깐 효율적으로 쿼리를 할 수 있습니다.
1. sqlite3_tokenizer_module 구조체 보기
struct sqlite3_tokenizer_module {
int iVersion;
int (*xCreate)(
int argc, /* Size of argv array */
const char *const*argv, /* Tokenizer argument strings */
sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
);
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
const char *pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
);
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
int (*xNext)(
sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
int *piStartOffset, /* OUT: Byte offset of token in input buffer */
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */
);
전체적으로 이렇습니다. 그냥 전체적인 구조 자체만 보면 될 것 같습니다.
그래도 나중에 취약점 발현 과정에 있어서 이해하기 좋게 xCreate 콜백에 대해서는 파악을 하고 가겠습니다.
/*
** Create a new tokenizer. The values in the argv[] array are the
** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
** TABLE statement that created the fts3 table. For example, if
** the following SQL is executed:
**
** CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
**
** then argc is set to 2, and the argv[] array contains pointers
** to the strings "arg1" and "arg2".
**
** This method should return either SQLITE_OK (0), or an SQLite error
** code. If SQLITE_OK is returned, then *ppTokenizer should be set
** to point at the newly created tokenizer structure. The generic
** sqlite3_tokenizer.pModule variable should not be initialized by
** this callback. The caller will do so.
*/
이건 소스코드에 있는 설명을 가지고 온 것입니다. xCreate()는 기본적으로 tokenizer를 새로 생성하는 과정이고, 이 함수가 실행이 되면, tokenizer에 접근하는 과정에서 이 포인터 값이 실행되는 것입니다. 그렇게 설정을 하고, 성공/실패 여부를 리턴합니다.
2. fts3_tokenizer 취약점
이 fts3_tokenizer는 custom으로 지정을 할 수 있다는 점에서 취약점이 발생합니다.
기본적인 로직은 다음과 같습니다.
1. 인자를 하나만 전달할 경우
SELECT fts3_tokenizer(<tokenizer-name>);
현재 등록되어있는 tokenizer가 구현된 pointer가 return
2. 인자를 두 개 이상 전달할 경우
SELECT fts3_tokenizer(<tokenizer-name>, <sqlite3_tokenizer_module ptr>);
새로 입력한 값이 tokenizer로 등록되고, 그 사본이 return
자, 그럼 이렇게 값을 집어넣으면 어떻게 될까요?
SELECT hex(fts3_tokenizer('simple'));
이러면 밑의 실습에서도 확인하겠지만, 해당 포인터의 값이 빅엔디안 방식으로 출력이 됩니다. 이게 첫 번째 취약점이에요.
뭐 실제로 주소를 쓰려면 리틀엔디안으로 바꿔야 한다는 점이 있지만, 그게 대수가 아니쥬
한 발 더 나아가 이렇게 하면 어떻게 될까요?
SELECT fts3_tokenizer('simple', x'4141414141414141');
CREATE VIRTUAL TABLE vt USING fts3 (content TEXT);
지금 두 번째 인자값을 그냥 주소로 넣어버린 모습입니다. 이렇게 할 경우, tokenizer의 주소가 아예 '0x4141414141414141'로 바뀌어버립니다. fts3_tokenizer는 콜백 함수 포인터의 유효성을 검증하지 않기 때문에, 두 번째 취약점인 Untrusted pointer dereference가 발생합니다.
아무튼 그런 상태에서 두 번째 명령어를 실행시킨다면? 0x4141414141414141에 접근하려고 노력하면서 프로그램이 segfault를 띄우게 됩니다.
이제 이게 저런 더미값이 아니라 함수의 주소라면?🤔🤔 a bit interesting...
이걸 이용한 CVE가 CVE-2015-7036입니다. 벌써 햇수로 6년이 지난 취약점인데, 으렵네여,,
3. 실습
[REDACTED]
참고
https://www2.sqlite.org/fts3.html
https://tourspace.tistory.com/395 << 참고로 이글 너무 잘 정리되어있습니당 시간나면 꼭 보시길.. (fts5기준인거 참고)
https://www.youtube.com/watch?v=N6MAhXfuv74&t=8s 21분즈음
'WEB' 카테고리의 다른 글
[WEB] SSTI(Server Side Template Injection) 취약점 (2) | 2021.08.05 |
---|---|
[Cloud] AWS IAM에 대하여 (3) | 2021.08.02 |
[웹 모의해킹-3] CSRF 실습 (0) | 2021.05.28 |
[웹 모의해킹-2] Command Injection 실습 : High, Impossible (0) | 2021.05.28 |
[웹 모의해킹-2] Command Injection 실습 : Medium (0) | 2021.05.27 |